Skip to content

Commit 1178ae7

Browse files
authored
Automates BGR images support test (#11299)
### Related https://linear.app/rerun/issue/RR-1120/replace-most-of-our-release-checklist-with-kittest ### What Automates BGR image support test, part of the manual release checklist. Checks all combinations of all int/float pixel formats, dark/light themes and alpha/opaque images. - [x] full-check
1 parent 3dde272 commit 1178ae7

26 files changed

+183
-93
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8965,6 +8965,7 @@ dependencies = [
89658965
"bytemuck",
89668966
"egui",
89678967
"glam",
8968+
"half",
89688969
"hexasphere",
89698970
"image",
89708971
"insta",

crates/viewer/re_view_spatial/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ re_component_ui.workspace = true
7474
re_test_context.workspace = true
7575
re_test_viewport.workspace = true
7676

77+
half.workspace = true
7778
insta.workspace = true
7879
ndarray.workspace = true
7980

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#![expect(clippy::unwrap_used)] // It's a test!
2+
3+
use image::GenericImageView as _;
4+
use itertools::Itertools as _;
5+
use re_chunk_store::RowId;
6+
use re_log_types::TimePoint;
7+
use re_test_context::{
8+
TestContext,
9+
external::egui_kittest::{OsThreshold, SnapshotOptions},
10+
};
11+
use re_test_viewport::TestContextExt as _;
12+
use re_types::{
13+
Archetype, blueprint::components::BackgroundKind, datatypes::ColorModel,
14+
image::ImageChannelType,
15+
};
16+
use re_viewer_context::{ViewClass as _, ViewId};
17+
use re_viewport_blueprint::ViewBlueprint;
18+
19+
fn convert_pixels_to<T: From<u8> + Copy>(u8s: &[u8]) -> Vec<T> {
20+
u8s.iter().map(|u| T::from(*u)).collect()
21+
}
22+
23+
fn run_bgr_test<T: ImageChannelType>(image: &[T], size: [u32; 2], color_model: ColorModel) {
24+
let mut test_context = TestContext::new_with_view_class::<re_view_spatial::SpatialView2D>();
25+
test_context.log_entity("bgr_image", |builder| {
26+
builder.with_archetype(
27+
RowId::new(),
28+
TimePoint::default(),
29+
&re_types::archetypes::Image::from_elements(image, size, color_model),
30+
)
31+
});
32+
33+
// Set up a view with a purple-ish background to catch alpha blending issues
34+
let view_id = test_context.setup_viewport_blueprint(|ctx, blueprint| {
35+
let view =
36+
ViewBlueprint::new_with_root_wildcard(re_view_spatial::SpatialView2D::identifier());
37+
let property_path = re_viewport_blueprint::entity_path_for_view_property(
38+
view.id,
39+
ctx.store_context.blueprint.tree(),
40+
re_types::blueprint::archetypes::Background::name(),
41+
);
42+
ctx.save_blueprint_archetype(
43+
property_path.clone(),
44+
&re_types::blueprint::archetypes::Background::new(BackgroundKind::SolidColor)
45+
.with_color(re_types::components::Color::from_rgb(200, 100, 200)),
46+
);
47+
blueprint.add_view_at_root(view)
48+
});
49+
50+
// type_name of half is "half::binary16::f16"
51+
let pixel_type_name = std::any::type_name::<T>().split("::").last().unwrap();
52+
53+
let snapshot_name = format!(
54+
"bgr_images_{}_{pixel_type_name}",
55+
color_model.to_string().to_lowercase(),
56+
);
57+
58+
run_view_ui_and_save_snapshot(
59+
&mut test_context,
60+
view_id,
61+
&snapshot_name,
62+
egui::vec2(160.0, 120.0),
63+
);
64+
}
65+
66+
fn run_view_ui_and_save_snapshot(
67+
test_context: &mut TestContext,
68+
view_id: ViewId,
69+
name: &str,
70+
size: egui::Vec2,
71+
) {
72+
let mut harness = test_context
73+
.setup_kittest_for_rendering()
74+
.with_size(size)
75+
.build_ui(|ui| {
76+
test_context.run_with_single_view(ui, view_id);
77+
});
78+
harness.run();
79+
harness.snapshot_options(
80+
name,
81+
&SnapshotOptions::new().failed_pixel_count_threshold(OsThreshold::new(2)),
82+
);
83+
}
84+
85+
fn run_all_formats(image: &[u8], size: [u32; 2], color_model: ColorModel) {
86+
run_bgr_test(image, size, color_model);
87+
run_bgr_test(&convert_pixels_to::<u16>(image), size, color_model);
88+
run_bgr_test(&convert_pixels_to::<u32>(image), size, color_model);
89+
run_bgr_test(&convert_pixels_to::<u64>(image), size, color_model);
90+
run_bgr_test(&convert_pixels_to::<i16>(image), size, color_model);
91+
run_bgr_test(&convert_pixels_to::<i32>(image), size, color_model);
92+
run_bgr_test(&convert_pixels_to::<i64>(image), size, color_model);
93+
run_bgr_test(&convert_pixels_to::<half::f16>(image), size, color_model);
94+
run_bgr_test(&convert_pixels_to::<f32>(image), size, color_model);
95+
run_bgr_test(&convert_pixels_to::<f64>(image), size, color_model);
96+
}
97+
98+
#[test]
99+
fn test_bgr_images() {
100+
let test_image =
101+
image::load_from_memory(include_bytes!("../../../../tests/assets/image/grinda.jpg"))
102+
.unwrap();
103+
let size = test_image.dimensions().into();
104+
let rgb_u8 = test_image.to_rgb8().into_raw();
105+
106+
let bgr_u8 = rgb_u8
107+
.chunks(3)
108+
.flat_map(|p| [p[2], p[1], p[0]])
109+
.collect_vec();
110+
run_all_formats(&bgr_u8, size, ColorModel::BGR);
111+
112+
let bgra_u8 = rgb_u8
113+
.chunks(3)
114+
.flat_map(|p| [p[2], p[1], p[0], 255])
115+
.collect_vec();
116+
run_all_formats(&bgra_u8, size, ColorModel::BGRA);
117+
}
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)