test(02-01): add linear-light correctness unit test to yazi-adapter

This commit is contained in:
Kanyin Cai 2026-03-21 13:35:13 +01:00
parent 189b785cc9
commit 875d9593b6
2 changed files with 52 additions and 3 deletions

View file

@ -35,7 +35,7 @@ Decimal phases appear between their surrounding integers in numeric order.
**Plans:** 1 plan
Plans:
- [ ] 01-01-PLAN.md — Replace resize pipeline with linear-light fast_image_resize (dependency + code + verification)
- [x] 01-01-PLAN.md — Replace resize pipeline with linear-light fast_image_resize (dependency + code + verification)
### Phase 2: Verification
**Goal**: The implementation is confirmed correct across all image types, protocols, and edge cases, with at least one automated correctness test
@ -46,7 +46,10 @@ Plans:
2. RGBA PNG with transparency displays without color fringing on transparent edges in IIP/WezTerm
3. EXIF-rotated JPEG (phone photo) previews with correct orientation and no quality regression
4. `cargo test --workspace` passes with the new test included
**Plans**: TBD
**Plans:** 1 plan
Plans:
- [ ] 02-01-PLAN.md — Add linear-light correctness unit test and visual verification checklist
## Progress
@ -56,4 +59,4 @@ Phases execute in numeric order: 1 -> 2
| Phase | Plans Complete | Status | Completed |
|-------|----------------|--------|-----------|
| 1. Implementation | 0/1 | Planned | - |
| 2. Verification | 0/TBD | Not started | - |
| 2. Verification | 0/1 | Planned | - |

View file

@ -197,3 +197,49 @@ impl Image {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use image::RgbImage;
#[test]
fn test_linear_light_gray_midpoint() {
// 50/50 mix of black and white pixels in a 4x4 checkerboard pattern.
// Averaging in sRGB space gives (0+255)/2 = 128.
// Averaging in linear-light space gives (0.0+1.0)/2 = 0.5,
// which maps back to sRGB ~186 via the gamma curve.
let black = image::Rgb([0u8, 0, 0]);
let white = image::Rgb([255u8, 255, 255]);
let mut img = RgbImage::new(4, 4);
for y in 0..4 {
for x in 0..4 {
let pixel = if (x + y) % 2 == 0 { white } else { black };
img.put_pixel(x, y, pixel);
}
}
let img = DynamicImage::from(img);
// Downscale to 1x1 using Lanczos3 (default filter)
let alg = ResizeAlg::Convolution(fast_image_resize::FilterType::Lanczos3);
let result = Image::fir_resize(img, 1, 1, alg).expect("fir_resize should succeed");
// Extract the single pixel
let rgb = result.to_rgb8();
let pixel = rgb.get_pixel(0, 0);
// Linear-light-correct downscaling of a 50/50 black+white checkerboard
// should yield ~186, because linear 0.5 maps to sRGB ~186.
//
// If we got ~128, the linearization pipeline is broken (sRGB-space averaging).
let tolerance = 5u8;
let expected = 186u8;
for (i, &channel) in pixel.0.iter().enumerate() {
assert!(
channel.abs_diff(expected) <= tolerance,
"channel {i}: expected ~{expected} (+/-{tolerance}), got {channel}. \
A value near 128 means sRGB linearization is not working."
);
}
}
}