Size a panorama's cell and thumbnail by class: two, three or four columns

One lookup, natural_span, maps a photograph's aspect to the columns its
cell spans, and the same number names its thumbnail class, Wide2, Wide3
or Wide4, 512 pixels of long edge per column, so a 4:1 panorama is as
sharp across four columns as a frame is in one. The boundaries are
where the two neighbouring cells would leave the same share of
themselves undrawn, sqrt(s(s+1)): 2.45 and 3.46, with the first at 1.9
so a 3:2 frame stays a frame. A grid too narrow for the class falls
back to the widest that fits, the tablet gives the whole row, and a
cell asks for the class it is actually drawn at: its span, but never
more than its own class. The merge renders each wide class up to the
composite's own, which covers every fallback.
This commit is contained in:
2026-09-28 19:57:37 -04:00
parent ae4e1a0f07
commit 6050a8e703
5 changed files with 155 additions and 56 deletions
+35 -15
View File
@@ -73,33 +73,51 @@ pub enum ThumbSize {
/// where something actually asks for that detail.
Large = 1,
/// TRACES: FR-MRG-6
/// A panorama's cell, which spans two or three columns — or the whole
/// row on the tablet — at the height of one. Its long edge is sized for
/// that width rather than for a square: the grid class of a 4:1
/// panorama is 256×64, a smear across three cells. Made only for
/// photographs that wide, so it costs a library nothing else.
Wide = 2,
/// A panorama's cell two columns wide, at the height of one: long edge
/// sized for the width rather than for a square, since the grid class
/// of a 4:1 panorama is 256×64 — a smear across the cells. The wide
/// classes are made only for photographs that wide, so they cost a
/// library nothing else.
Wide2 = 2,
/// Three columns.
Wide3 = 3,
/// Four columns: the widest class.
Wide4 = 4,
}
/// The most columns a wide class spans.
pub const WIDEST_SPAN: usize = 4;
impl ThumbSize {
/// Long edge in pixels.
/// Long edge in pixels. A wide class is 512 per column it spans, which
/// keeps its short edge near the large class's for the aspect that
/// class is chosen for — sharp at the largest cells on a 2x display.
pub fn edge(self) -> u32 {
match self {
ThumbSize::Grid => 256,
ThumbSize::Large => 1024,
ThumbSize::Wide => 2048,
ThumbSize::Wide2 => 1024,
ThumbSize::Wide3 => 1536,
ThumbSize::Wide4 => 2048,
}
}
/// The wide class for a cell `span` columns wide: `None` for one
/// column, and the widest class for anything past it.
pub fn wide(span: usize) -> Option<Self> {
match span {
0 | 1 => None,
2 => Some(ThumbSize::Wide2),
3 => Some(ThumbSize::Wide3),
_ => Some(ThumbSize::Wide4),
}
}
/// The class for a cell `span` columns wide whose columns are drawn at
/// `pixels`: the wide class for any cell wider than one, whatever the
/// `pixels`: a wide class for any cell wider than one, whatever the
/// zoom, since its height is a column's and its width is not.
pub fn for_span(span: usize, pixels: u32) -> Self {
if span > 1 {
ThumbSize::Wide
} else {
Self::for_cell(pixels)
}
Self::wide(span).unwrap_or_else(|| Self::for_cell(pixels))
}
/// The smallest class that can fill a cell of this size without visibly
@@ -121,7 +139,9 @@ impl ThumbSize {
match v {
0 => Some(ThumbSize::Grid),
1 => Some(ThumbSize::Large),
2 => Some(ThumbSize::Wide),
2 => Some(ThumbSize::Wide2),
3 => Some(ThumbSize::Wide3),
4 => Some(ThumbSize::Wide4),
_ => None,
}
}