1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
//! Posterizes small blocks of raw pixels to the best-matching Unicode block-element glyph.
//!
//! This is the "subcell" technique used by `doryen-rs` (`blit_2x`), libtcod, and notcurses'
//! blitter chain to render raster images as text without a tileset: split a source image into
//! one small pixel block per terminal cell, then pick whichever glyph plus foreground/background
//! color pair best reconstructs that block. Three block shapes are supported, in increasing
//! fidelity (and decreasing terminal compatibility):
//!
//! - [`quantize_half_block`]: 1x2 pixels -> `' '`/`▀`/`▄`/`█` (Unicode Block Elements, supported
//! almost everywhere monospace fonts render at all).
//! - [`quantize_quadrant`]: 2x2 pixels -> the 16 quadrant block characters (`▘▝▀▖▌▞▛...`).
//! - [`quantize_sextant`]: 2x3 pixels -> the 64 "Symbols for Legacy Computing" sextant
//! characters, doubling vertical resolution again over quadrants. Newest and least
//! universally supported of the three (a 2022 Unicode addition).
//!
//! Callers own the fallback chain: probe terminal/font support (or just take a caller-supplied
//! capability flag) and call whichever function matches, sampling the source image at that
//! function's pixel geometry. There's no single "auto-detect and degrade" entry point here,
//! matching every other terminal-capability decision in retroglyph (e.g. `egc` support):
//! detection policy lives with the backend, not with this pure geometry/color utility.
//!
//! # Algorithm
//!
//! For an N-pixel block, every one of the `2^N` ways to split the block into a "foreground set"
//! and "background set" is scored: average the two sets' colors, then sum each pixel's squared
//! distance to whichever average it was assigned to. The split with the lowest total error wins,
//! and its bit pattern selects the glyph directly (the glyph tables below are indexed by that
//! same pattern, foreground bits set, read row-major). This exhaustive search is cheap here (at
//! most 64 candidates, 6 pixels each, for [`quantize_sextant`]) and is the same technique
//! notcurses' blitter chain documents using for its own 3x2 sextant solver.
//!
//! Ties (multiple patterns reconstructing a block with equally minimal error) resolve to the
//! lower-numbered pattern, matching the tie-break convention `retroglyph_core::color`'s own
//! nearest-color search already uses. Two tie shapes come up often enough to call out: a flat,
//! single-color block ties across every pattern (all give zero error) and always resolves to
//! pattern `0`, the cheapest glyph, a plain space colored by `bg`. And any block with exactly
//! two distinct pixel colors has exactly two zero-error patterns, one the bitwise complement of
//! the other (swap which color is called `fg` and which is `bg` and the reconstruction is
//! identical); the lower pattern number wins there too.
//!
//! # Provenance
//!
//! The glyph tables ([`HALF_BLOCKS`], [`QUADRANTS`], [`SEXTANTS`]) are adapted from
//! [ratatui-core's `symbols::pixel` module](https://github.com/ratatui/ratatui/blob/main/ratatui-core/src/symbols/pixel.rs)
//! (MIT-licensed, like retroglyph), which lists them by bit pattern rather than by Unicode
//! codepoint order: the sextant block in particular is not contiguous or monotonic in Unicode
//! (four combinations reuse the pre-existing Block Elements `' '`, `█`, `▌`, `▐` instead of
//! having their own Legacy Computing codepoints), so a hand-rolled table is where subtle,
//! hard-to-spot-in-review bugs live. Reusing a table already exercised by a widely-used library
//! is deliberate risk reduction, not just convenience.
//!
//! # Example
//!
//! ```
//! use retroglyph_core::subcell::quantize_quadrant;
//!
//! // A block that's white in the top-left corner, black everywhere else.
//! let black = (0, 0, 0);
//! let white = (255, 255, 255);
//! let glyph = quantize_quadrant([white, black, black, black]);
//! assert_eq!(glyph.ch, '▘'); // top-left quadrant
//! assert_eq!(glyph.fg, retroglyph_core::Color::Rgb { r: 255, g: 255, b: 255 });
//! assert_eq!(glyph.bg, retroglyph_core::Color::Rgb { r: 0, g: 0, b: 0 });
//! ```
use crateColor;
/// A raw 24-bit RGB pixel sample: `(r, g, b)`, one byte per channel.
pub type Rgb = ;
/// The Unicode Block Elements glyphs for a 1-wide x 2-tall pixel block, indexed by a 2-bit
/// pattern (bit 0 = top pixel set, bit 1 = bottom pixel set).
pub const HALF_BLOCKS: = ;
/// The 16 quadrant block glyphs for a 2x2 pixel block, indexed by a 4-bit pattern in row-major
/// order (bit 0 = top-left, bit 1 = top-right, bit 2 = bottom-left, bit 3 = bottom-right).
///
/// Adapted from [ratatui-core's `symbols::pixel::QUADRANTS`][ratatui].
///
/// [ratatui]: https://github.com/ratatui/ratatui/blob/main/ratatui-core/src/symbols/pixel.rs
pub const QUADRANTS: = ;
/// The 64 sextant glyphs for a 2x3 pixel block.
///
/// Indexed by a 6-bit pattern in row-major order (bit 0 = top-left, bit 1 = top-right, bit 2 =
/// mid-left, bit 3 = mid-right, bit 4 = bottom-left, bit 5 = bottom-right). Mostly from Unicode's
/// "Symbols for Legacy Computing" block; adapted from [ratatui-core's
/// `symbols::pixel::SEXTANTS`][ratatui].
///
/// [ratatui]: https://github.com/ratatui/ratatui/blob/main/ratatui-core/src/symbols/pixel.rs
pub const SEXTANTS: = ;
/// A posterized pixel block: the best-matching glyph plus its foreground and background colors.
///
/// The background color is only meaningful for glyphs that don't cover the full cell (anything
/// but `' '` and `'█'`); the foreground color is only meaningful for glyphs other than `' '`.
/// Both are still populated for those edge cases (as the block's overall average color) so a
/// caller never has to special-case `Glyph` before styling a cell with it.
/// Averages the `pixels` selected by `mask` (bit `i` set means `pixels[i]` is included), rounding
/// each channel to the nearest integer. Returns `None` if `mask` selects no pixels.
/// Posterizes `pixels` to the glyph (from `table`, indexed by row-major bit pattern) and two
/// representative colors that minimize total squared color error, by exhaustive search over
/// every `2^N` way to split the block into a foreground and background set.
///
/// `table.len()` must be `2^pixels.len()`; every caller in this module upholds that by
/// construction, so this stays a plain slice rather than a const-generic-sized array (which
/// would need unstable `generic_const_exprs` to relate `N` to `table`'s length at the type
/// level).
/// Posterizes a 1-wide x 2-tall pixel block (`[top, bottom]`) to `' '`/`▀`/`▄`/`█` plus two
/// representative colors.
///
/// This is the lowest-fidelity, most compatible option: plain Unicode Block Elements, supported
/// by essentially every monospace terminal font.
///
/// See the `16_subcell_image` example for `quantize_half_block` in action:
/// <https://main.retroglyph.dev/examples/16_subcell_image/terminal/>.
///
/// # Examples
///
/// ```
/// use retroglyph_core::subcell::quantize_half_block;
///
/// let glyph = quantize_half_block([(255, 255, 255), (0, 0, 0)]);
/// assert_eq!(glyph.ch, '▀'); // top half set, bottom clear
/// ```
///
/// Never panics: `pixels` is a fixed-size 2-element array, so there is no length to validate
/// and no index into it that can be out of bounds.
/// Posterizes a 2x2 pixel block (`[top_left, top_right, bottom_left, bottom_right]`) to one of
/// the 16 quadrant block glyphs plus two representative colors.
///
/// Doubles both horizontal and vertical resolution over [`quantize_half_block`].
///
/// On the bundled pixel backends (`retroglyph-software`, `retroglyph-gl`), rendering these
/// glyphs correctly requires a font that actually declares coverage for the quadrant block
/// characters: CP437 has no mapping for them. A font built with `retroglyph_window`'s
/// `BitmapFont::new` (CP437-only) renders every quadrant glyph as a solid block. Supply quadrant
/// coverage by passing either a primary font or a `BitmapFont::with_charset` fallback in a
/// `FontChain` to those backends' `font()` builder method; the glyph then takes the cell's
/// foreground color, which a tileset sprite (the other way to draw a non-CP437 shape) does not.
///
/// See the `16_subcell_image` example for `quantize_quadrant` in action:
/// <https://main.retroglyph.dev/examples/16_subcell_image/terminal/>.
///
/// # Examples
///
/// ```
/// use retroglyph_core::subcell::quantize_quadrant;
///
/// let black = (0, 0, 0);
/// let white = (255, 255, 255);
/// let glyph = quantize_quadrant([black, white, black, black]);
/// assert_eq!(glyph.ch, '▝'); // top-right quadrant
/// ```
///
/// Never panics: `pixels` is a fixed-size 4-element array, so there is no length to validate
/// and no index into it that can be out of bounds.
/// Posterizes a 2-wide x 3-tall pixel block (`[top_left, top_right, mid_left, mid_right,
/// bottom_left, bottom_right]`) to one of the 64 sextant glyphs plus two representative colors.
///
/// The highest-fidelity option (doubles vertical resolution again over [`quantize_quadrant`]),
/// and the newest/least universally supported: sextant glyphs come from a 2022 Unicode addition
/// and need a font with "Symbols for Legacy Computing" coverage to render as blocks rather than
/// tofu/replacement characters.
///
/// On the bundled pixel backends (`retroglyph-software`, `retroglyph-gl`), that coverage has to
/// come from the font: CP437 has no mapping for sextant glyphs at all, so a font built with
/// `retroglyph_window`'s `BitmapFont::new` (CP437-only) renders every sextant glyph as a solid
/// block. Supply sextant coverage by passing either a primary font or a `BitmapFont::with_charset`
/// fallback in a `FontChain` to those backends' `font()` builder method; the glyph then takes the
/// cell's foreground color, which a tileset sprite (the other way to draw a non-CP437 shape) does
/// not.
///
/// See the `16_subcell_image` example for `quantize_sextant` in action:
/// <https://main.retroglyph.dev/examples/16_subcell_image/terminal/>.
///
/// # Examples
///
/// ```
/// use retroglyph_core::subcell::quantize_sextant;
///
/// let black = (0, 0, 0);
/// let white = (255, 255, 255);
/// let glyph = quantize_sextant([white, black, black, black, black, black]);
/// assert_eq!(glyph.ch, '🬀'); // top-left sextant only
/// ```
///
/// Never panics: `pixels` is a fixed-size 6-element array, so there is no length to validate
/// and no index into it that can be out of bounds.