diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 1cf6ff37..883f6878 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -1610,7 +1610,7 @@ impl DocumentMessageHandler { } /// Loads layer resources such as creating the blob URLs for the images and loading all of the fonts in the document - pub fn load_layer_resources(&self, responses: &mut VecDeque, root: &LayerDataType, mut path: Vec, document_id: u64) { + pub fn load_layer_resources(&self, responses: &mut VecDeque, root: &LayerDataType, mut path: Vec, _document_id: u64) { fn walk_layers(data: &LayerDataType, path: &mut Vec, responses: &mut VecDeque, fonts: &mut HashSet) { match data { LayerDataType::Folder(folder) => { diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs index d7f10eba..c182557e 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs @@ -633,14 +633,15 @@ fn static_nodes() -> Vec { DocumentNodeType { name: "Brightness/Contrast", category: "Image Adjustments", - identifier: NodeImplementation::proto("graphene_core::raster::BrightnessContrastNode<_, _>"), + identifier: NodeImplementation::proto("graphene_core::raster::BrightnessContrastNode<_, _, _>"), inputs: vec![ DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true), DocumentInputType::value("Brightness", TaggedValue::F64(0.), false), DocumentInputType::value("Contrast", TaggedValue::F64(0.), false), + DocumentInputType::value("Use Legacy", TaggedValue::Bool(false), false), ], outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], - properties: node_properties::brighten_image_properties, + properties: node_properties::brightness_contrast_properties, }, DocumentNodeType { name: "Threshold", diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs index dc37dd0b..625d44eb 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs @@ -501,11 +501,16 @@ pub fn adjust_hsl_properties(document_node: &DocumentNode, node_id: NodeId, _con ] } -pub fn brighten_image_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { - let brightness = number_widget(document_node, node_id, 1, "Brightness", NumberInput::default().min(-255.).max(255.), true); - let contrast = number_widget(document_node, node_id, 2, "Contrast", NumberInput::default().min(-255.).max(255.), true); +pub fn brightness_contrast_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { + let brightness = number_widget(document_node, node_id, 1, "Brightness", NumberInput::default().min(-150.).max(150.), true); + let contrast = number_widget(document_node, node_id, 2, "Contrast", NumberInput::default().min(-100.).max(100.), true); + let use_legacy = bool_widget(document_node, node_id, 3, "Use Legacy", true); - vec![LayoutGroup::Row { widgets: brightness }, LayoutGroup::Row { widgets: contrast }] + vec![ + LayoutGroup::Row { widgets: brightness }, + LayoutGroup::Row { widgets: contrast }, + LayoutGroup::Row { widgets: use_legacy }, + ] } pub fn blur_image_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { diff --git a/node-graph/gcore/src/raster.rs b/node-graph/gcore/src/raster.rs index 8ea20f40..5b5c7177 100644 --- a/node-graph/gcore/src/raster.rs +++ b/node-graph/gcore/src/raster.rs @@ -5,10 +5,11 @@ use crate::Node; #[cfg(target_arch = "spirv")] use spirv_std::num_traits::float::Float; -pub mod color; pub use self::color::Color; pub mod adjustments; +pub mod brightness_contrast; +pub mod color; pub use adjustments::*; #[derive(Debug, Default)] diff --git a/node-graph/gcore/src/raster/adjustments.rs b/node-graph/gcore/src/raster/adjustments.rs index a657b2bc..4cc71047 100644 --- a/node-graph/gcore/src/raster/adjustments.rs +++ b/node-graph/gcore/src/raster/adjustments.rs @@ -285,6 +285,7 @@ pub use hue_shift::HueSaturationNode; #[cfg(not(target_arch = "spirv"))] mod hue_shift { use super::*; + #[derive(Debug)] pub struct HueSaturationNode { hue_shift: Hue, @@ -461,21 +462,6 @@ fn vibrance_node(color: Color, vibrance: f64) -> Color { altered_color.to_gamma_srgb() } -#[derive(Debug, Clone, Copy)] -pub struct BrightnessContrastNode { - brightness: Brightness, - contrast: Contrast, -} - -// From https://stackoverflow.com/questions/2976274/adjust-bitmap-image-brightness-contrast-using-c -#[node_macro::node_fn(BrightnessContrastNode)] -fn adjust_image_brightness_and_contrast(color: Color, brightness: f64, contrast: f64) -> Color { - let (brightness, contrast) = (brightness as f32, contrast as f32); - let factor = (259. * (contrast + 255.)) / (255. * (259. - contrast)); - let channel = |channel: f32| ((factor * (channel * 255. + brightness - 128.) + 128.) / 255.).clamp(0., 1.); - color.map_rgb(channel) -} - #[derive(Debug, Clone, Copy)] pub struct OpacityNode { opacity_multiplier: O, @@ -524,5 +510,6 @@ fn exposure(color: Color, exposure: f64, offset: f64, gamma_correction: f64) -> .gamma(gamma_correction as f32) .map_rgb(|c: f32| c.clamp(0., 1.)); + // TODO: Remove conversion to linear when the whole node graph uses linear color result.to_gamma_srgb() } diff --git a/node-graph/gcore/src/raster/brightness_contrast.rs b/node-graph/gcore/src/raster/brightness_contrast.rs new file mode 100644 index 00000000..eda63912 --- /dev/null +++ b/node-graph/gcore/src/raster/brightness_contrast.rs @@ -0,0 +1,355 @@ +use crate::{Color, Node}; + +// LEGACY BRIGHTNESS/CONTRAST + +pub struct BrightnessContrastLegacyMapperNode { + contrast: f32, + combined: f32, +} + +impl<'i> Node<'i, Color> for BrightnessContrastLegacyMapperNode { + type Output = Color; + + fn eval<'s: 'i>(&'s self, color: Color) -> Color { + color.map_rgb(|c| (c + c * self.contrast + self.combined).clamp(0., 1.)) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct GenerateBrightnessContrastLegacyMapperNode { + brightness: Brightness, + contrast: Contrast, +} + +#[node_macro::node_fn(GenerateBrightnessContrastLegacyMapperNode)] +fn brightness_contrast_legacy_node(_primary: (), brightness: f32, contrast: f32) -> BrightnessContrastLegacyMapperNode { + let brightness = brightness / 255.; + + let contrast = contrast / 100.; + let contrast = if contrast > 0. { (contrast * std::f32::consts::FRAC_PI_2 - 0.01).tan() } else { contrast }; + + let combined = brightness * contrast + brightness - contrast / 2.; + + BrightnessContrastLegacyMapperNode { contrast, combined } +} + +// NORMAL BRIGHTNESS/CONTRAST + +pub struct BrightnessContrastMapperNode { + combined_lut: [f32; WINDOW_SIZE], +} + +impl<'i> Node<'i, Color> for BrightnessContrastMapperNode { + type Output = Color; + + fn eval<'s: 'i>(&'s self, color: Color) -> Color { + color.map_rgb(|c| { + let index_in_combined_lut = (c * (self.combined_lut.len() - 1) as f32).round() as usize; + self.combined_lut[index_in_combined_lut] + }) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct GenerateBrightnessContrastMapperNode { + brightness: Brightness, + contrast: Contrast, +} + +#[node_macro::node_fn(GenerateBrightnessContrastMapperNode)] +fn brightness_contrast_node(_primary: (), brightness: f32, contrast: f32) -> BrightnessContrastMapperNode { + // Brightness LUT + let brightness_is_negative = brightness < 0.; + let brightness = brightness.abs() / 100.; + let brightness_curve_points = CubicSplines { + x: [0., 130. - brightness * 26., 233. - brightness * 48., 255.].map(|x| x / 255.), + y: [0., 130. + brightness * 51., 233. + brightness * 10., 255.].map(|x| x / 255.), + }; + let brightness_curve_solutions = solve_cubic_splines(&brightness_curve_points); + let mut brightness_lut: [f32; WINDOW_SIZE] = core::array::from_fn(|i| { + let x = i as f32 / (WINDOW_SIZE as f32 - 1.); + interpolate_cubic_splines(x, &brightness_curve_points, &brightness_curve_solutions) + }); + // Special handling for when brightness is negative + if brightness_is_negative { + brightness_lut = core::array::from_fn(|i| { + let mut x = i; + while x > 1 && brightness_lut[x] > i as f32 / WINDOW_SIZE as f32 { + x -= 1; + } + x as f32 / WINDOW_SIZE as f32 + }); + } + + // Contrast LUT + let contrast = contrast / 100.; + let contrast_curve_points = CubicSplines { + x: [0., 64., 192., 255.].map(|x| x / 255.), + y: [0., 64. - contrast * 30., 192. + contrast * 30., 255.].map(|x| x / 255.), + }; + let contrast_curve_solutions = solve_cubic_splines(&contrast_curve_points); + let contrast_lut: [f32; WINDOW_SIZE] = core::array::from_fn(|i| { + let x = i as f32 / (WINDOW_SIZE as f32 - 1.); + interpolate_cubic_splines(x, &contrast_curve_points, &contrast_curve_solutions) + }); + + // Composed brightness and contrast LUTs + let combined_lut = brightness_lut.map(|brightness| { + let index_in_contrast_lut = (brightness * (contrast_lut.len() - 1) as f32).round() as usize; + contrast_lut[index_in_contrast_lut] + }); + + BrightnessContrastMapperNode { combined_lut } +} + +const WINDOW_SIZE: usize = 1024; + +struct CubicSplines { + x: [f32; 4], + y: [f32; 4], +} + +fn solve_cubic_splines(cubic_spline_values: &CubicSplines) -> [f32; 4] { + let (x, y) = (&cubic_spline_values.x, &cubic_spline_values.y); + + // Build an augmented matrix to solve the system of equations using Gaussian elimination + let mut augmented_matrix = [ + [ + 2. / (x[1] - x[0]), + 1. / (x[1] - x[0]), + 0., + 0., + // | + 3. * (y[1] - y[0]) / ((x[1] - x[0]) * (x[1] - x[0])), + ], + [ + 1. / (x[1] - x[1 - 1]), + 2. * (1. / (x[1] - x[1 - 1]) + 1. / (x[1 + 1] - x[1])), + 1. / (x[1 + 1] - x[1]), + 0., + // | + 3. * ((y[1] - y[1 - 1]) / ((x[1] - x[1 - 1]) * (x[1] - x[1 - 1])) + (y[1 + 1] - y[1]) / ((x[1 + 1] - x[1]) * (x[1 + 1] - x[1]))), + ], + [ + 0., + 1. / (x[2] - x[2 - 1]), + 2. * (1. / (x[2] - x[2 - 1]) + 1. / (x[2 + 1] - x[2])), + 1. / (x[2 + 1] - x[2]), + // | + 3. * ((y[2] - y[2 - 1]) / ((x[2] - x[2 - 1]) * (x[2] - x[2 - 1])) + (y[2 + 1] - y[2]) / ((x[2 + 1] - x[2]) * (x[2 + 1] - x[2]))), + ], + [ + 0., + 0., + 1. / (x[3] - x[2]), + 2. / (x[3] - x[2]), + // | + 3. * (y[3] - y[2]) / ((x[3] - x[2]) * (x[3] - x[2])), + ], + ]; + + // Gaussian elimination: forward elimination + for row in 0..4 { + let pivot_row_index = (row..4) + .max_by(|&a_row, &b_row| { + augmented_matrix[a_row][row] + .abs() + .partial_cmp(&augmented_matrix[b_row][row].abs()) + .unwrap_or(core::cmp::Ordering::Equal) + }) + .unwrap(); + + // Swap the current row with the row that has the largest pivot element + augmented_matrix.swap(row, pivot_row_index); + + // Eliminate the current column in all rows below the current one + for row_below_current in row + 1..4 { + assert!(augmented_matrix[row][row].abs() > std::f32::EPSILON); + + let scale_factor = augmented_matrix[row_below_current][row] / augmented_matrix[row][row]; + for col in row..5 { + augmented_matrix[row_below_current][col] -= augmented_matrix[row][col] * scale_factor + } + } + } + + // Gaussian elimination: back substitution + let mut solutions = [0.; 4]; + for col in (0..4).rev() { + assert!(augmented_matrix[col][col].abs() > std::f32::EPSILON); + + solutions[col] = augmented_matrix[col][4] / augmented_matrix[col][col]; + + for row in (0..col).rev() { + augmented_matrix[row][4] -= augmented_matrix[row][col] * solutions[col]; + augmented_matrix[row][col] = 0.; + } + } + + solutions +} + +fn interpolate_cubic_splines(input: f32, points: &CubicSplines, solutions: &[f32]) -> f32 { + if input <= points.x[0] { + return points.y[0]; + } + if input >= points.x[points.x.len() - 1] { + return points.y[points.x.len() - 1]; + } + + // Find the segment that the input falls between + let mut segment = 1; + while points.x[segment] < input { + segment += 1; + } + let segment_start = segment - 1; + let segment_end = segment; + + // Calculate the output value using quadratic interpolation + let input_value = points.x[segment_start]; + let input_value_prev = points.x[segment_end]; + let output_value = points.y[segment_start]; + let output_value_prev = points.y[segment_end]; + let solutions_value = solutions[segment_start]; + let solutions_value_prev = solutions[segment_end]; + + let output_delta = solutions_value_prev * (input_value - input_value_prev) - (output_value - output_value_prev); + let solution_delta = (output_value - output_value_prev) - solutions_value * (input_value - input_value_prev); + + let input_ratio = (input - input_value_prev) / (input_value - input_value_prev); + let prev_output_ratio = (1. - input_ratio) * output_value_prev; + let output_ratio = input_ratio * output_value; + let quadratic_ratio = input_ratio * (1. - input_ratio) * (output_delta * (1. - input_ratio) + solution_delta * input_ratio); + + let result = prev_output_ratio + output_ratio + quadratic_ratio; + result.clamp(0., 1.) +} + +mod tests { + use crate::value::ClonedNode; + + #[allow(unused_imports)] + use super::*; + + #[test] + fn brightness_contrast_legacy_tests() { + let brightness_contrast_legacy_map = |brightness, contrast| -> [u8; 256] { + let brightness = ClonedNode::new(brightness); + let contrast = ClonedNode::new(contrast); + let generate_brightness_contrast_legacy_node = GenerateBrightnessContrastLegacyMapperNode::new(brightness, contrast); + let mapper = generate_brightness_contrast_legacy_node.eval(()); + + let color = |r| Color::from_rgbaf32_unchecked(r, 0., 0., 1.); + + core::array::from_fn(|x| (mapper.eval(color(x as f32 / 255.)).r() * 255.).round() as u8) + }; + + assert_eq!(brightness_contrast_legacy_map(-150., 100.), [0; 256]); + assert_eq!(brightness_contrast_legacy_map(-77., 100.), { + let mut x = [0; 204].into_iter().chain([77, 178].into_iter()).chain([255; 50].into_iter()); + core::array::from_fn(|_| x.next().unwrap()) + }); + assert_eq!(brightness_contrast_legacy_map(0., 100.), { + let mut x = [0; 127].into_iter().chain([77, 178].into_iter()).chain([255; 127].into_iter()); + core::array::from_fn(|_| x.next().unwrap()) + }); + assert_eq!(brightness_contrast_legacy_map(53., 100.), { + let mut x = [0; 74].into_iter().chain([77, 178].into_iter()).chain([255; 180].into_iter()); + core::array::from_fn(|_| x.next().unwrap()) + }); + assert_eq!(brightness_contrast_legacy_map(150., 100.), [255; 256]); + assert_eq!(brightness_contrast_legacy_map(-150., 0.), { + let mut x = [0; 151].into_iter().chain(1..); + core::array::from_fn(|_| x.next().unwrap()) + }); + assert_eq!(brightness_contrast_legacy_map(-77., 0.), { + let mut x = [0; 78].into_iter().chain(1..); + core::array::from_fn(|_| x.next().unwrap()) + }); + assert_eq!(brightness_contrast_legacy_map(0., 0.), core::array::from_fn(|i| i as u8)); + assert_eq!(brightness_contrast_legacy_map(53., 0.), core::array::from_fn(|i| (i + 53).min(255) as u8)); + assert_eq!(brightness_contrast_legacy_map(150., 0.), core::array::from_fn(|i| (i + 150).min(255) as u8)); + assert_eq!(brightness_contrast_legacy_map(-150., -100.), [128; 256]); + assert_eq!(brightness_contrast_legacy_map(-77., -100.), [128; 256]); + assert_eq!(brightness_contrast_legacy_map(0., -100.), [128; 256]); + assert_eq!(brightness_contrast_legacy_map(53., -100.), [128; 256]); + assert_eq!(brightness_contrast_legacy_map(150., -100.), [128; 256]); + } + + #[test] + fn brightness_contrast_tests() { + let brightness_contrast_map = |brightness, contrast| -> [u8; 1024] { + let brightness = ClonedNode::new(brightness); + let contrast = ClonedNode::new(contrast); + let generate_brightness_contrast_node = GenerateBrightnessContrastMapperNode::new(brightness, contrast); + let mapper = generate_brightness_contrast_node.eval(()); + + let color = |r| Color::from_rgbaf32_unchecked(r, 0., 0., 1.); + + core::array::from_fn(|x| (mapper.eval(color(x as f32 / 1023.)).r() * 255.).round() as u8) + }; + + let string_data = |string: &str| string.split(',').map(|s| s.parse().unwrap()).collect::>(); + + assert_eq!( + &brightness_contrast_map(-150., 100.), + string_data("0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,30,30,31,31,31,31,31,31,31,31,31,32,32,32,32,32,32,32,32,33,33,33,33,33,33,33,33,34,34,34,34,34,34,34,34,34,35,35,35,35,35,35,35,35,36,36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,38,38,38,38,38,39,39,39,39,39,39,39,39,40,40,40,40,40,40,40,40,41,41,41,41,41,41,42,42,42,42,42,42,42,43,43,43,43,43,43,44,44,44,44,44,45,45,45,45,45,45,45,46,46,46,46,46,46,47,47,47,47,47,48,48,48,48,48,48,48,49,49,49,49,49,50,50,50,50,50,51,51,51,51,51,52,52,52,52,52,53,53,53,53,53,54,54,54,54,54,55,55,55,55,55,56,56,56,56,56,57,57,57,57,57,58,58,58,58,59,59,59,59,59,60,60,60,60,61,61,61,61,61,62,62,62,63,63,63,63,64,64,64,64,65,65,65,65,66,66,66,66,66,67,67,68,68,68,68,69,69,69,69,70,70,70,71,71,71,71,72,72,72,73,73,73,74,74,74,74,75,75,75,76,76,76,77,77,77,78,78,78,79,79,79,80,80,80,81,81,82,82,82,83,83,83,84,84,84,85,85,86,86,86,87,87,88,88,88,89,89,90,90,91,91,92,92,92,93,93,94,94,95,95,96,96,96,97,98,98,99,99,99,100,101,101,101,102,103,103,104,104,105,106,106,107,107,108,108,109,110,110,111,111,112,113,114,114,115,116,116,117,118,119,119,120,121,122,122,123,124,125,125,126,127,128,129,130,131,131,132,133,134,135,136,137,139,140,141,142,143,144,146,147,148,150,151,152,154,155,157,159,160,162,164,166,167,169,171,173,175,177,180,182,184,186,189,191,194,196,199,201,204,206,209,212,214,217,220,223,225,228,231,234,236,239,242,244,247,250,252").as_slice() + ); + assert_eq!( + brightness_contrast_map(-77., 100.), + string_data("0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,29,29,29,29,29,29,30,30,30,30,30,30,30,30,31,31,31,31,31,31,32,32,32,32,32,32,33,33,33,33,33,33,33,34,34,34,34,34,34,35,35,35,35,35,35,36,36,36,36,36,36,37,37,37,37,37,37,38,38,38,38,38,39,39,39,39,39,39,40,40,40,40,40,40,41,41,41,41,42,42,42,42,42,42,43,43,43,43,43,44,44,44,44,45,45,45,45,45,45,46,46,46,46,46,47,47,47,47,48,48,48,48,48,48,49,49,49,49,50,50,50,50,50,51,51,51,51,52,52,52,52,53,53,53,53,53,54,54,54,54,55,55,55,55,56,56,56,56,56,57,57,57,57,58,58,58,58,59,59,59,59,60,60,60,60,60,61,61,61,61,62,62,62,63,63,63,63,64,64,64,64,65,65,65,65,66,66,66,66,67,67,67,68,68,68,68,69,69,69,69,70,70,70,71,71,71,71,72,72,72,72,73,73,73,74,74,74,74,75,75,75,75,76,76,77,77,77,77,78,78,78,78,79,79,79,80,80,80,80,81,81,82,82,82,82,83,83,83,84,84,84,84,85,85,86,86,86,86,87,87,87,88,88,88,88,89,89,90,90,90,90,91,91,92,92,92,92,93,93,93,94,94,94,95,95,95,96,96,96,96,97,97,98,98,99,99,99,99,100,100,101,101,101,101,102,102,103,103,103,103,104,104,105,105,105,106,106,106,107,107,107,108,108,108,109,109,110,110,110,111,111,111,112,112,113,113,113,114,114,114,115,115,116,116,116,117,117,117,118,118,119,119,119,119,120,120,121,121,122,122,122,123,123,124,124,124,125,125,125,126,126,127,127,128,128,128,128,129,129,130,130,131,131,131,131,132,132,133,133,133,134,134,135,135,136,136,136,137,137,138,138,139,139,139,140,140,141,141,142,142,142,143,143,144,144,144,145,145,146,146,147,147,148,148,149,149,150,150,150,151,151,152,152,152,153,153,154,155,155,155,156,156,157,157,157,158,159,159,159,160,160,161,161,161,162,163,163,164,164,164,165,166,166,166,167,167,168,168,169,169,170,170,171,171,172,172,173,173,174,174,175,175,175,176,177,177,177,178,179,179,179,180,180,181,181,182,182,183,183,184,184,185,185,186,186,187,188,188,188,189,189,190,190,191,192,192,193,193,194,194,195,195,195,196,196,197,198,198,199,199,200,200,201,201,202,202,203,204,204,204,205,205,206,207,207,208,208,209,209,210,210,211,211,212,212,213,214,214,214,215,216,216,217,217,218,218,219,219,220,220,221,221,222,222,223,224,224,225,225,226,226,227,227,228,228,229,229,230,230,231,232,232,233,233,234,234,235,235,236,236,237,237,238,238,239,239,240,240,241,241,242,242,243,243,244,244,245,246,246,246,247,247,248,248,249,249,250,250,251,252,252,252,253,253,254,254").as_slice() + ); + assert_eq!( + brightness_contrast_map(0., 100.), + string_data("0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,16,16,16,16,16,16,16,17,17,17,17,17,17,18,18,18,18,18,18,18,19,19,19,19,19,19,20,20,20,20,20,20,21,21,21,21,21,22,22,22,22,22,22,23,23,23,23,23,24,24,24,24,24,25,25,25,25,25,26,26,26,26,26,27,27,27,27,27,28,28,28,28,28,29,29,29,29,30,30,30,30,30,31,31,31,31,32,32,32,32,33,33,33,33,34,34,34,34,35,35,35,35,36,36,36,36,37,37,37,37,38,38,38,39,39,39,39,40,40,40,40,41,41,41,42,42,42,42,43,43,43,44,44,44,45,45,45,45,46,46,46,47,47,47,48,48,48,48,49,49,49,50,50,50,51,51,51,52,52,52,53,53,53,54,54,54,55,55,55,56,56,56,57,57,57,58,58,58,59,59,59,60,60,60,61,61,61,62,62,63,63,63,64,64,64,65,65,65,66,66,66,67,67,68,68,68,69,69,69,70,70,71,71,71,72,72,72,73,73,74,74,74,75,75,75,76,76,77,77,77,78,78,78,79,79,80,80,80,81,81,82,82,82,83,83,84,84,84,85,85,86,86,86,87,87,88,88,88,89,89,90,90,90,91,91,92,92,92,93,93,94,94,94,95,95,96,96,96,97,97,98,98,99,99,99,100,100,101,101,101,102,102,103,103,103,104,104,105,105,106,106,106,107,107,108,108,108,109,109,110,110,111,111,111,112,112,113,113,114,114,114,115,115,116,116,117,117,117,118,118,119,119,119,120,120,121,121,122,122,122,123,123,124,124,125,125,125,126,126,127,127,128,128,128,129,129,130,130,131,131,131,132,132,133,133,133,134,134,135,135,136,136,136,137,137,138,138,139,139,139,140,140,141,141,142,142,142,143,143,144,144,144,145,145,146,146,147,147,147,148,148,149,149,150,150,150,151,151,152,152,152,153,153,154,154,155,155,155,156,156,157,157,157,158,158,159,159,159,160,160,161,161,161,162,162,163,163,164,164,164,165,165,166,166,166,167,167,168,168,168,169,169,170,170,170,171,171,172,172,172,173,173,174,174,174,175,175,175,176,176,177,177,177,178,178,179,179,179,180,180,180,181,181,182,182,182,183,183,183,184,184,185,185,185,186,186,186,187,187,188,188,188,189,189,189,190,190,190,191,191,192,192,192,193,193,193,194,194,194,195,195,195,196,196,196,197,197,198,198,198,199,199,199,200,200,200,201,201,201,202,202,202,203,203,203,204,204,204,204,205,205,205,206,206,206,207,207,207,208,208,208,209,209,209,209,210,210,210,211,211,211,212,212,212,212,213,213,213,214,214,214,214,215,215,215,216,216,216,216,217,217,217,218,218,218,218,219,219,219,219,220,220,220,220,221,221,221,221,222,222,222,222,223,223,223,223,224,224,224,224,225,225,225,225,225,226,226,226,226,227,227,227,227,227,228,228,228,228,229,229,229,229,229,230,230,230,230,230,231,231,231,231,231,232,232,232,232,232,233,233,233,233,233,233,234,234,234,234,234,234,235,235,235,235,235,236,236,236,236,236,236,236,237,237,237,237,237,237,238,238,238,238,238,238,239,239,239,239,239,239,239,240,240,240,240,240,240,240,241,241,241,241,241,241,241,241,242,242,242,242,242,242,242,242,243,243,243,243,243,243,243,243,244,244,244,244,244,244,244,244,245,245,245,245,245,245,245,245,245,246,246,246,246,246,246,246,246,246,246,247,247,247,247,247,247,247,247,247,247,248,248,248,248,248,248,248,248,248,248,248,249,249,249,249,249,249,249,249,249,249,249,250,250,250,250,250,250,250,250,250,250,250,250,251,251,251,251,251,251,251,251,251,251,251,251,252,252,252,252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253,253,253,253,253,253,253,253,253,254,254,254,254,254,254,254,254,254,254,254,254,254,254,255,255,255,255,255,255,255").as_slice() + ); + assert_eq!( + brightness_contrast_map(53., 100.), + string_data("0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,9,9,9,9,9,9,10,10,10,10,10,10,10,11,11,11,11,11,11,12,12,12,12,12,13,13,13,13,13,13,14,14,14,14,14,14,15,15,15,15,15,16,16,16,16,16,17,17,17,17,18,18,18,18,18,19,19,19,19,20,20,20,20,21,21,21,21,22,22,22,22,23,23,23,24,24,24,24,25,25,25,25,26,26,26,27,27,27,27,28,28,28,29,29,29,30,30,30,30,31,31,32,32,32,33,33,33,34,34,34,35,35,35,36,36,36,37,37,37,38,38,39,39,39,40,40,40,41,41,42,42,42,43,43,44,44,45,45,45,46,46,47,47,48,48,48,49,49,50,50,51,51,51,52,52,53,53,54,54,55,55,56,56,57,57,57,58,58,59,59,60,60,61,61,62,62,63,63,64,64,65,65,66,66,66,67,68,68,69,69,69,70,71,71,72,72,73,73,74,74,75,75,76,76,77,77,78,78,79,79,80,80,81,81,82,82,83,84,84,84,85,86,86,87,87,88,88,89,89,90,90,91,92,92,92,93,94,94,95,95,96,96,97,97,98,99,99,100,100,101,101,102,102,103,103,104,104,105,106,106,107,107,108,108,109,109,110,111,111,111,112,113,113,114,114,115,115,116,117,117,118,118,119,119,120,120,121,122,122,122,123,124,124,125,125,126,126,127,128,128,128,129,130,130,131,131,132,132,133,133,134,134,135,136,136,136,137,138,138,139,139,140,140,141,142,142,142,143,144,144,144,145,146,146,147,147,147,148,149,149,150,150,151,151,152,152,153,153,154,155,155,155,156,156,157,157,158,158,159,159,160,160,161,161,162,162,163,163,164,164,165,165,166,166,167,167,168,168,169,169,170,170,170,171,172,172,172,173,174,174,174,175,175,175,176,177,177,177,178,179,179,179,180,180,180,181,182,182,182,183,183,184,184,185,185,185,186,186,187,187,188,188,188,189,189,190,190,190,191,191,192,192,193,193,193,194,194,194,195,195,196,196,196,197,197,198,198,199,199,199,200,200,200,201,201,201,202,202,203,203,203,204,204,204,204,205,205,206,206,206,207,207,207,208,208,208,209,209,209,210,210,210,211,211,211,212,212,212,212,213,213,213,214,214,214,215,215,215,216,216,216,216,217,217,217,218,218,218,218,219,219,219,219,220,220,220,220,221,221,221,221,222,222,222,222,223,223,223,223,224,224,224,224,225,225,225,225,225,226,226,226,226,227,227,227,227,227,228,228,228,228,229,229,229,229,229,230,230,230,230,230,230,231,231,231,231,231,232,232,232,232,232,233,233,233,233,233,233,233,234,234,234,234,234,234,234,235,235,235,235,235,236,236,236,236,236,236,236,236,237,237,237,237,237,237,237,238,238,238,238,238,238,238,239,239,239,239,239,239,239,239,239,240,240,240,240,240,240,240,240,241,241,241,241,241,241,241,241,241,241,242,242,242,242,242,242,242,242,242,242,243,243,243,243,243,243,243,243,243,243,243,244,244,244,244,244,244,244,244,244,244,244,245,245,245,245,245,245,245,245,245,245,245,245,246,246,246,246,246,246,246,246,246,246,246,246,246,246,246,247,247,247,247,247,247,247,247,247,247,247,247,247,247,247,247,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255").as_slice() + ); + assert_eq!( + brightness_contrast_map(150., 100.), + string_data("0,0,0,1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,8,8,8,8,9,9,9,10,10,10,10,11,11,11,12,12,12,13,13,13,14,14,14,15,15,16,16,16,17,17,18,18,18,19,19,20,20,21,21,22,22,23,23,24,24,25,25,26,26,27,27,28,28,29,30,30,31,31,32,33,33,34,35,35,36,37,37,38,39,40,40,41,42,42,43,44,45,46,46,47,48,49,50,50,51,52,53,54,55,56,56,57,58,59,60,61,62,63,64,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,78,80,80,82,82,84,84,86,86,88,88,90,90,92,92,93,94,95,96,97,99,99,101,101,102,103,104,106,106,107,108,109,111,111,112,114,114,116,117,117,119,119,120,122,122,123,125,125,127,128,128,129,131,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,150,152,152,153,154,155,156,157,158,159,159,161,161,162,163,164,165,166,167,168,168,169,170,171,172,173,174,174,175,176,177,177,178,179,180,180,181,182,183,183,184,185,186,187,188,188,189,189,190,191,192,192,193,194,194,195,196,196,197,198,199,199,200,200,201,202,202,203,204,204,205,205,206,206,207,208,208,209,209,210,210,211,211,212,212,213,213,214,214,215,215,216,216,217,217,218,218,219,219,220,220,220,221,221,222,222,222,223,223,224,224,224,225,225,225,226,226,226,227,227,227,228,228,229,229,229,229,230,230,230,231,231,231,231,232,232,232,233,233,233,233,233,234,234,234,234,235,235,235,235,236,236,236,236,236,237,237,237,237,237,238,238,238,238,238,239,239,239,239,239,239,240,240,240,240,240,240,241,241,241,241,241,241,241,241,242,242,242,242,242,242,242,242,243,243,243,243,243,243,243,243,244,244,244,244,244,244,244,244,245,245,245,245,245,245,245,245,245,245,245,246,246,246,246,246,246,246,246,246,246,246,246,247,247,247,247,247,247,247,247,247,247,247,247,247,247,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255").as_slice() + ); + assert_eq!( + brightness_contrast_map(-150., 0.), + string_data("0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,30,30,30,31,31,31,31,31,31,31,31,31,31,32,32,32,32,32,32,32,32,32,32,33,33,33,33,33,33,33,33,33,33,34,34,34,34,34,34,34,34,34,34,35,35,35,35,35,35,35,35,35,35,35,36,36,36,36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37,37,38,38,38,38,38,38,38,38,38,38,39,39,39,39,39,39,39,39,39,39,40,40,40,40,40,40,40,40,40,41,41,41,41,41,41,41,41,41,41,42,42,42,42,42,42,42,42,42,42,43,43,43,43,43,43,43,43,43,43,44,44,44,44,44,44,44,44,44,45,45,45,45,45,45,45,45,45,45,46,46,46,46,46,46,46,46,46,47,47,47,47,47,47,47,47,47,47,48,48,48,48,48,48,48,48,48,49,49,49,49,49,49,49,49,49,50,50,50,50,50,50,50,50,50,50,51,51,51,51,51,51,51,51,51,52,52,52,52,52,52,52,52,52,53,53,53,53,53,53,53,53,53,54,54,54,54,54,54,54,54,54,55,55,55,55,55,55,55,55,55,56,56,56,56,56,56,56,56,56,57,57,57,57,57,57,57,57,57,58,58,58,58,58,58,58,58,59,59,59,59,59,59,59,59,59,60,60,60,60,60,60,60,60,61,61,61,61,61,61,61,61,61,62,62,62,62,62,62,62,62,63,63,63,63,63,63,63,63,64,64,64,64,64,64,64,64,64,65,65,65,65,65,65,65,65,66,66,66,66,66,66,66,66,67,67,67,67,67,67,67,67,68,68,68,68,68,68,68,69,69,69,69,69,69,69,69,70,70,70,70,70,70,70,70,71,71,71,71,71,71,71,72,72,72,72,72,72,72,72,73,73,73,73,73,73,73,74,74,74,74,74,74,74,75,75,75,75,75,75,75,75,76,76,76,76,76,76,76,77,77,77,77,77,77,77,78,78,78,78,78,78,79,79,79,79,79,79,79,80,80,80,80,80,80,80,81,81,81,81,81,81,82,82,82,82,82,82,82,83,83,83,83,83,83,84,84,84,84,84,84,85,85,85,85,85,85,85,85,86,86,86,86,86,86,87,87,87,87,87,87,88,88,88,88,88,89,89,89,89,89,89,90,90,90,90,90,91,91,91,91,91,91,92,92,92,92,92,93,93,93,93,93,94,94,94,94,94,95,95,95,95,95,96,96,96,96,96,97,97,97,97,98,98,98,98,98,99,99,99,99,100,100,100,100,101,101,101,101,101,102,102,102,102,103,103,103,103,104,104,104,104,105,105,105,106,106,106,106,107,107,107,107,108,108,108,109,109,109,109,110,110,110,111,111,111,112,112,112,113,113,113,114,114,114,115,115,115,116,116,116,117,117,117,118,118,119,119,119,120,120,121,121,121,122,122,123,123,124,124,125,125,125,126,126,127,127,128,128,129,129,130,130,131,132,132,133,133,134,135,135,136,137,138,138,139,140,141,142,142,143,144,145,146,147,148,149,150,151,153,154,155,157,158,159,161,162,164,166,167,169,170,172,174,176,178,180,183,185,187,190,193,196,199,202,206,210,214,219,224,231,238,246").as_slice() + ); + assert_eq!( + brightness_contrast_map(-77., 0.), + string_data("0,0,0,0,0,0,1,1,1,1,1,1,2,2,2,2,2,2,2,3,3,3,3,3,3,3,4,4,4,4,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6,6,7,7,7,7,7,7,7,8,8,8,8,8,8,8,9,9,9,9,9,9,9,10,10,10,10,10,10,10,11,11,11,11,11,11,12,12,12,12,12,12,12,13,13,13,13,13,13,13,14,14,14,14,14,14,14,15,15,15,15,15,15,16,16,16,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,19,19,19,19,19,19,20,20,20,20,20,20,20,21,21,21,21,21,21,21,22,22,22,22,22,22,22,23,23,23,23,23,23,24,24,24,24,24,24,24,25,25,25,25,25,25,25,26,26,26,26,26,26,26,27,27,27,27,27,27,28,28,28,28,28,28,28,29,29,29,29,29,29,29,30,30,30,30,30,30,31,31,31,31,31,31,31,32,32,32,32,32,32,32,33,33,33,33,33,33,34,34,34,34,34,34,34,35,35,35,35,35,35,36,36,36,36,36,36,36,37,37,37,37,37,37,37,38,38,38,38,38,38,39,39,39,39,39,39,39,40,40,40,40,40,40,41,41,41,41,41,41,41,42,42,42,42,42,42,43,43,43,43,43,43,43,44,44,44,44,44,44,45,45,45,45,45,45,45,46,46,46,46,46,46,47,47,47,47,47,47,47,48,48,48,48,48,48,49,49,49,49,49,49,49,50,50,50,50,50,50,51,51,51,51,51,51,52,52,52,52,52,52,52,53,53,53,53,53,53,54,54,54,54,54,54,55,55,55,55,55,55,55,56,56,56,56,56,56,57,57,57,57,57,57,58,58,58,58,58,58,58,59,59,59,59,59,59,60,60,60,60,60,60,61,61,61,61,61,61,62,62,62,62,62,62,63,63,63,63,63,63,63,64,64,64,64,64,64,65,65,65,65,65,65,66,66,66,66,66,66,67,67,67,67,67,67,68,68,68,68,68,68,69,69,69,69,69,69,70,70,70,70,70,70,71,71,71,71,71,71,72,72,72,72,72,72,73,73,73,73,73,73,74,74,74,74,74,74,75,75,75,75,75,75,76,76,76,76,76,76,77,77,77,77,77,77,78,78,78,78,78,78,79,79,79,79,79,80,80,80,80,80,80,81,81,81,81,81,81,82,82,82,82,82,82,83,83,83,83,83,84,84,84,84,84,84,85,85,85,85,85,85,85,86,86,86,86,86,86,87,87,87,87,87,88,88,88,88,88,88,89,89,89,89,89,90,90,90,90,90,90,91,91,91,91,91,92,92,92,92,92,92,93,93,93,93,93,94,94,94,94,94,94,95,95,95,95,95,96,96,96,96,96,97,97,97,97,97,97,98,98,98,98,98,99,99,99,99,99,100,100,100,100,100,101,101,101,101,101,101,102,102,102,102,102,103,103,103,103,103,104,104,104,104,104,105,105,105,105,105,106,106,106,106,106,107,107,107,107,107,108,108,108,108,108,109,109,109,109,109,110,110,110,110,110,111,111,111,111,112,112,112,112,112,113,113,113,113,113,114,114,114,114,114,115,115,115,115,116,116,116,116,116,117,117,117,117,117,118,118,118,118,119,119,119,119,119,120,120,120,120,121,121,121,121,121,122,122,122,122,123,123,123,123,123,124,124,124,124,125,125,125,125,126,126,126,126,126,127,127,127,127,128,128,128,128,128,129,129,129,129,129,130,130,130,130,131,131,131,131,132,132,132,132,133,133,133,133,134,134,134,134,135,135,135,135,136,136,136,136,137,137,137,137,138,138,138,138,139,139,139,140,140,140,140,141,141,141,141,142,142,142,142,143,143,143,144,144,144,144,145,145,145,145,146,146,146,147,147,147,147,148,148,148,149,149,149,149,150,150,150,151,151,151,151,152,152,152,153,153,153,154,154,154,154,155,155,155,156,156,156,157,157,157,158,158,158,159,159,159,160,160,160,160,161,161,161,162,162,162,163,163,163,164,164,165,165,165,166,166,166,167,167,167,168,168,168,169,169,170,170,170,170,171,171,171,172,172,173,173,173,174,174,175,175,175,176,176,177,177,177,178,178,179,179,180,180,180,181,181,182,182,183,183,184,184,184,185,185,186,186,187,187,188,188,189,189,190,190,191,191,192,192,193,194,194,195,195,196,196,197,198,198,199,200,200,201,201,202,203,203,204,205,206,206,207,208,209,209,210,211,212,213,214,214,215,216,217,218,219,220,221,222,223,224,226,227,228,229,230,232,233,234,236,237,239,240,242,243,245,246,248,250,251,253").as_slice() + ); + assert_eq!( + brightness_contrast_map(0., 0.), + string_data("0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,8,8,8,8,9,9,9,9,10,10,10,10,11,11,11,11,12,12,12,12,13,13,13,13,14,14,14,14,15,15,15,15,16,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,20,20,20,20,21,21,21,21,22,22,22,22,23,23,23,23,24,24,24,24,25,25,25,25,26,26,26,26,27,27,27,27,28,28,28,28,29,29,29,29,30,30,30,30,31,31,31,31,32,32,32,32,33,33,33,33,34,34,34,34,35,35,35,35,36,36,36,36,37,37,37,37,38,38,38,38,39,39,39,39,40,40,40,40,41,41,41,41,42,42,42,42,43,43,43,43,44,44,44,44,45,45,45,45,46,46,46,46,47,47,47,47,48,48,48,48,49,49,49,49,50,50,50,50,51,51,51,51,52,52,52,52,53,53,53,53,54,54,54,54,55,55,55,55,56,56,56,56,57,57,57,57,58,58,58,58,59,59,59,59,60,60,60,60,61,61,61,61,62,62,62,62,63,63,63,63,64,64,64,64,65,65,65,65,66,66,66,66,67,67,67,67,68,68,68,68,69,69,69,69,70,70,70,70,71,71,71,71,72,72,72,72,73,73,73,73,74,74,74,74,75,75,75,75,76,76,76,76,77,77,77,77,78,78,78,78,79,79,79,79,80,80,80,80,81,81,81,81,82,82,82,82,83,83,83,83,84,84,84,84,85,85,85,85,85,86,86,86,86,87,87,87,87,88,88,88,88,89,89,89,89,90,90,90,90,91,91,91,91,92,92,92,92,93,93,93,93,94,94,94,94,95,95,95,95,96,96,96,96,97,97,97,97,98,98,98,98,99,99,99,99,100,100,100,100,101,101,101,101,102,102,102,102,103,103,103,103,104,104,104,104,105,105,105,105,106,106,106,106,107,107,107,107,108,108,108,108,109,109,109,109,110,110,110,110,111,111,111,111,112,112,112,112,113,113,113,113,114,114,114,114,115,115,115,115,116,116,116,116,117,117,117,117,118,118,118,118,119,119,119,119,120,120,120,120,121,121,121,121,122,122,122,122,123,123,123,123,124,124,124,124,125,125,125,125,126,126,126,126,127,127,127,127,128,128,128,128,129,129,129,129,130,130,130,130,131,131,131,131,132,132,132,132,133,133,133,133,134,134,134,134,135,135,135,135,136,136,136,136,137,137,137,137,138,138,138,138,139,139,139,139,140,140,140,140,141,141,141,141,142,142,142,142,143,143,143,143,144,144,144,144,145,145,145,145,146,146,146,146,147,147,147,147,148,148,148,148,149,149,149,149,150,150,150,150,151,151,151,151,152,152,152,152,153,153,153,153,154,154,154,154,155,155,155,155,156,156,156,156,157,157,157,157,158,158,158,158,159,159,159,159,160,160,160,160,161,161,161,161,162,162,162,162,163,163,163,163,164,164,164,164,165,165,165,165,166,166,166,166,167,167,167,167,168,168,168,168,169,169,169,169,170,170,170,170,170,171,171,171,171,172,172,172,172,173,173,173,173,174,174,174,174,175,175,175,175,176,176,176,176,177,177,177,177,178,178,178,178,179,179,179,179,180,180,180,180,181,181,181,181,182,182,182,182,183,183,183,183,184,184,184,184,185,185,185,185,186,186,186,186,187,187,187,187,188,188,188,188,189,189,189,189,190,190,190,190,191,191,191,191,192,192,192,192,193,193,193,193,194,194,194,194,195,195,195,195,196,196,196,196,197,197,197,197,198,198,198,198,199,199,199,199,200,200,200,200,201,201,201,201,202,202,202,202,203,203,203,203,204,204,204,204,205,205,205,205,206,206,206,206,207,207,207,207,208,208,208,208,209,209,209,209,210,210,210,210,211,211,211,211,212,212,212,212,213,213,213,213,214,214,214,214,215,215,215,215,216,216,216,216,217,217,217,217,218,218,218,218,219,219,219,219,220,220,220,220,221,221,221,221,222,222,222,222,223,223,223,223,224,224,224,224,225,225,225,225,226,226,226,226,227,227,227,227,228,228,228,228,229,229,229,229,230,230,230,230,231,231,231,231,232,232,232,232,233,233,233,233,234,234,234,234,235,235,235,235,236,236,236,236,237,237,237,237,238,238,238,238,239,239,239,239,240,240,240,240,241,241,241,241,242,242,242,242,243,243,243,243,244,244,244,244,245,245,245,245,246,246,246,246,247,247,247,247,248,248,248,248,249,249,249,249,250,250,250,250,251,251,251,251,252,252,252,252,253,253,253,253,254,254,254,254,255,255,255").as_slice() + ); + assert_eq!( + brightness_contrast_map(53., 0.), + string_data("0,0,1,1,1,2,2,2,3,3,3,4,4,5,5,5,6,6,6,7,7,7,8,8,8,9,9,10,10,10,11,11,11,12,12,12,13,13,14,14,14,15,15,15,16,16,16,17,17,17,18,18,19,19,19,20,20,20,21,21,21,22,22,22,23,23,24,24,24,25,25,25,26,26,26,27,27,27,28,28,29,29,29,30,30,30,31,31,31,32,32,32,33,33,34,34,34,35,35,35,36,36,36,37,37,37,38,38,39,39,39,40,40,40,41,41,41,42,42,42,43,43,44,44,44,45,45,45,46,46,46,47,47,47,48,48,48,49,49,50,50,50,51,51,51,52,52,52,53,53,53,54,54,54,55,55,56,56,56,57,57,57,58,58,58,59,59,59,60,60,60,61,61,62,62,62,63,63,63,64,64,64,65,65,65,66,66,66,67,67,67,68,68,69,69,69,70,70,70,71,71,71,72,72,72,73,73,73,74,74,74,75,75,75,76,76,76,77,77,78,78,78,79,79,79,80,80,80,81,81,81,82,82,82,83,83,83,84,84,84,85,85,85,86,86,86,87,87,87,88,88,88,89,89,89,90,90,90,91,91,91,92,92,92,93,93,93,94,94,94,95,95,95,96,96,96,97,97,97,98,98,98,99,99,99,100,100,100,101,101,101,102,102,102,103,103,103,104,104,104,105,105,105,106,106,106,107,107,107,108,108,108,109,109,109,110,110,110,111,111,111,112,112,112,113,113,113,114,114,114,115,115,115,116,116,116,117,117,117,118,118,118,119,119,119,120,120,120,120,121,121,121,122,122,122,123,123,123,124,124,124,125,125,125,126,126,126,127,127,127,128,128,128,129,129,129,129,130,130,130,131,131,131,132,132,132,133,133,133,134,134,134,135,135,135,135,136,136,136,137,137,137,138,138,138,139,139,139,139,140,140,140,141,141,141,142,142,142,143,143,143,144,144,144,144,145,145,145,146,146,146,147,147,147,147,148,148,148,149,149,149,150,150,150,150,151,151,151,152,152,152,153,153,153,153,154,154,154,155,155,155,156,156,156,156,157,157,157,158,158,158,159,159,159,159,160,160,160,161,161,161,161,162,162,162,163,163,163,163,164,164,164,165,165,165,165,166,166,166,167,167,167,167,168,168,168,169,169,169,169,170,170,170,170,171,171,171,171,172,172,172,173,173,173,173,174,174,174,175,175,175,175,176,176,176,176,177,177,177,178,178,178,178,179,179,179,179,180,180,180,180,181,181,181,182,182,182,182,183,183,183,183,184,184,184,184,185,185,185,186,186,186,186,187,187,187,187,188,188,188,188,189,189,189,189,190,190,190,190,191,191,191,191,192,192,192,192,193,193,193,193,194,194,194,194,195,195,195,195,196,196,196,196,197,197,197,197,198,198,198,198,199,199,199,199,200,200,200,200,200,201,201,201,201,202,202,202,202,203,203,203,203,204,204,204,204,204,205,205,205,205,206,206,206,206,207,207,207,207,207,208,208,208,208,209,209,209,209,209,210,210,210,210,210,211,211,211,211,212,212,212,212,212,213,213,213,213,213,214,214,214,214,215,215,215,215,215,216,216,216,216,216,217,217,217,217,217,218,218,218,218,218,219,219,219,219,219,220,220,220,220,220,221,221,221,221,221,221,222,222,222,222,222,223,223,223,223,223,224,224,224,224,224,224,225,225,225,225,225,226,226,226,226,226,226,227,227,227,227,227,227,228,228,228,228,228,228,229,229,229,229,229,229,230,230,230,230,230,230,231,231,231,231,231,231,232,232,232,232,232,232,232,233,233,233,233,233,233,234,234,234,234,234,234,234,235,235,235,235,235,235,235,236,236,236,236,236,236,236,237,237,237,237,237,237,237,238,238,238,238,238,238,238,238,239,239,239,239,239,239,239,239,240,240,240,240,240,240,240,240,241,241,241,241,241,241,241,241,241,242,242,242,242,242,242,242,242,242,243,243,243,243,243,243,243,243,243,243,244,244,244,244,244,244,244,244,244,244,245,245,245,245,245,245,245,245,245,245,246,246,246,246,246,246,246,246,246,246,246,247,247,247,247,247,247,247,247,247,247,247,248,248,248,248,248,248,248,248,248,248,248,248,249,249,249,249,249,249,249,249,249,249,249,249,249,250,250,250,250,250,250,250,250,250,250,250,250,250,251,251,251,251,251,251,251,251,251,251,251,251,251,252,252,252,252,252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,254,254,254,254,254,254,254,254,254,254,254,254,254,254,255,255,255,255,255,255,255,255,255,255").as_slice() + ); + assert_eq!( + brightness_contrast_map(150., 0.), + string_data("0,1,1,2,3,3,4,5,5,6,7,7,8,9,9,10,11,11,12,13,14,14,15,16,16,17,18,18,19,20,20,21,22,22,23,24,24,25,26,26,27,28,29,29,30,31,31,32,33,33,34,35,35,36,37,37,38,39,39,40,41,41,42,43,43,44,45,45,46,47,47,48,49,49,50,51,51,52,53,53,54,55,55,56,57,57,58,59,59,60,61,61,62,63,63,64,65,65,66,67,67,68,69,69,70,71,71,72,73,73,74,75,75,76,76,77,78,78,79,80,80,81,82,82,83,84,84,85,85,86,87,87,88,88,89,90,90,91,92,92,93,94,94,95,95,96,97,97,98,99,99,100,100,101,102,102,103,104,104,105,105,106,107,107,108,108,109,110,110,111,112,112,113,113,114,115,115,116,116,117,118,118,119,119,120,121,121,122,122,123,123,124,125,125,126,126,127,128,128,129,129,130,130,131,132,132,133,133,134,134,135,136,136,137,137,138,138,139,140,140,141,141,142,142,143,143,144,145,145,146,146,147,147,148,148,149,149,150,151,151,152,152,153,153,154,154,155,155,156,156,157,157,158,158,159,159,160,160,161,161,162,162,163,164,164,165,165,166,166,166,167,167,168,168,169,169,170,170,171,171,172,172,173,173,173,174,174,175,175,176,176,177,177,178,178,179,179,180,180,180,181,181,182,182,183,183,184,184,184,185,185,186,186,187,187,187,188,188,189,189,190,190,190,191,191,192,192,192,193,193,194,194,194,195,195,196,196,196,197,197,197,198,198,199,199,199,200,200,200,201,201,202,202,202,203,203,203,204,204,204,205,205,205,206,206,206,207,207,207,208,208,208,209,209,209,210,210,210,211,211,211,212,212,212,212,213,213,213,214,214,214,215,215,215,215,216,216,216,217,217,217,217,218,218,218,218,219,219,219,219,220,220,220,220,221,221,221,221,222,222,222,222,223,223,223,223,224,224,224,224,225,225,225,225,225,226,226,226,226,226,227,227,227,227,228,228,228,228,228,229,229,229,229,229,229,230,230,230,230,230,231,231,231,231,231,231,232,232,232,232,232,233,233,233,233,233,233,234,234,234,234,234,234,234,235,235,235,235,235,235,235,236,236,236,236,236,236,236,237,237,237,237,237,237,237,238,238,238,238,238,238,238,238,239,239,239,239,239,239,239,239,239,240,240,240,240,240,240,240,240,240,241,241,241,241,241,241,241,241,241,241,242,242,242,242,242,242,242,242,242,242,242,243,243,243,243,243,243,243,243,243,243,243,243,244,244,244,244,244,244,244,244,244,244,244,244,244,244,245,245,245,245,245,245,245,245,245,245,245,245,245,245,245,245,246,246,246,246,246,246,246,246,246,246,246,246,246,246,246,246,246,246,247,247,247,247,247,247,247,247,247,247,247,247,247,247,247,247,247,247,247,247,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255").as_slice() + ); + assert_eq!( + brightness_contrast_map(-150., -100.), + string_data("0,0,0,0,0,0,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,5,5,5,5,5,6,6,6,6,6,6,6,6,7,7,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,10,10,10,10,10,10,11,11,11,11,11,11,11,11,12,12,12,12,12,13,13,13,13,13,13,14,14,14,14,14,14,14,14,15,15,15,15,15,15,16,16,16,16,16,17,17,17,17,17,17,17,17,18,18,18,18,18,18,19,19,19,19,19,19,19,19,20,20,20,20,20,21,21,21,21,21,21,22,22,22,22,22,22,22,22,23,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,26,26,26,26,26,27,27,27,27,27,27,27,27,28,28,28,28,28,29,29,29,29,29,29,29,29,29,30,30,30,30,30,31,31,31,31,31,31,31,31,32,32,32,32,32,33,33,33,33,33,33,34,34,34,34,34,34,34,34,35,35,35,35,35,36,36,36,36,36,36,36,36,37,37,37,37,37,38,38,38,38,38,38,38,38,39,39,39,39,39,40,40,40,40,40,40,40,40,41,41,41,41,41,41,42,42,42,42,42,42,42,43,43,43,43,43,43,44,44,44,44,44,44,44,44,45,45,45,45,45,46,46,46,46,46,46,46,46,47,47,47,47,47,47,47,48,48,48,48,48,48,49,49,49,49,49,49,49,50,50,50,50,50,50,51,51,51,51,51,51,51,52,52,52,52,52,53,53,53,53,53,53,53,53,54,54,54,54,54,54,54,54,55,55,55,55,55,56,56,56,56,56,56,56,57,57,57,57,57,57,57,57,58,58,58,58,58,59,59,59,59,59,59,59,59,60,60,60,60,60,60,60,61,61,61,61,61,61,61,61,62,62,62,62,62,63,63,63,63,63,63,63,64,64,64,64,64,64,64,65,65,65,65,65,65,65,65,66,66,66,66,66,66,66,67,67,67,67,67,67,67,68,68,68,68,68,69,69,69,69,69,69,69,69,70,70,70,70,70,70,70,71,71,71,71,71,71,71,72,72,72,72,72,72,72,73,73,73,73,73,73,73,74,74,74,74,74,74,74,75,75,75,75,75,75,75,76,76,76,76,76,76,76,76,77,77,77,77,77,77,78,78,78,78,78,78,78,78,78,78,79,79,79,79,79,79,79,80,80,80,80,80,80,81,81,81,81,81,81,81,82,82,82,82,82,82,82,82,82,83,83,83,83,83,83,83,84,84,84,84,84,84,84,85,85,85,85,85,85,85,85,85,86,86,86,86,86,86,87,87,87,87,87,87,87,87,87,88,88,88,88,88,88,89,89,89,89,89,89,89,89,89,90,90,90,90,90,90,90,90,91,91,91,91,91,91,91,92,92,92,92,92,92,92,92,93,93,93,93,93,93,93,93,94,94,94,94,94,94,94,94,94,95,95,95,95,95,95,95,95,96,96,96,96,96,96,96,96,96,96,97,97,97,97,97,97,97,97,98,98,98,98,98,98,98,99,99,99,99,99,99,99,99,99,99,100,100,100,100,100,100,100,100,101,101,101,101,101,101,101,101,101,102,102,102,102,102,102,102,102,102,103,103,103,103,103,103,103,103,103,104,104,104,104,104,104,104,104,104,105,105,105,105,105,105,105,105,105,105,105,106,106,106,106,106,106,106,106,107,107,107,107,107,107,107,107,107,107,108,108,108,108,108,108,108,108,108,108,109,109,109,109,109,109,109,109,109,110,110,110,110,110,110,110,110,110,110,110,111,111,111,111,111,111,111,111,111,112,112,112,112,112,112,112,112,112,112,113,113,113,113,113,113,113,113,113,113,114,114,114,114,114,114,114,114,114,114,114,115,115,115,115,115,115,115,115,115,115,116,116,116,116,116,116,116,116,116,116,117,117,117,117,117,117,117,117,117,117,118,118,118,118,118,118,118,118,118,118,119,119,119,119,119,119,119,119,119,119,119,120,120,120,120,120,120,120,120,120,121,121,121,121,121,121,121,121,121,121,122,122,122,122,122,122,122,122,122,122,123,123,123,123,123,123,123,123,123,124,124,124,124,124,124,124,124,124,125,125,125,125,125,125,125,125,125,126,126,126,126,126,126,126,127,127,127,127,127,127,127,127,128,128,128,128,128,128,129,129,129,129,129,129,130,130,130,130,130,131,131,131,131,132,132,132,132,133,133,134,134,134,135,135,135,136,136,137,138,138,139,140,140,141,142,143,144,145,146,147,149,150,152,154,155,158,160,163,166,169,173,178,183,189,196,204,214,226,240").as_slice() + ); + assert_eq!( + brightness_contrast_map(-77., -100.), + string_data("0,0,0,0,1,1,1,2,2,2,2,3,3,3,3,3,4,4,4,5,5,5,5,6,6,6,6,6,7,7,7,8,8,8,8,8,9,9,9,9,10,10,10,11,11,11,11,11,12,12,12,12,13,13,13,14,14,14,14,14,15,15,15,16,16,16,16,17,17,17,17,17,18,18,18,19,19,19,19,19,20,20,20,20,21,21,21,22,22,22,22,22,23,23,23,23,24,24,24,24,24,25,25,25,26,26,26,27,27,27,27,27,27,28,28,28,29,29,29,29,29,30,30,30,31,31,31,31,31,32,32,32,32,33,33,33,34,34,34,34,34,35,35,35,35,36,36,36,36,36,37,37,37,38,38,38,38,38,39,39,39,40,40,40,40,40,41,41,41,41,42,42,42,42,42,43,43,43,44,44,44,44,44,45,45,45,45,46,46,46,46,46,47,47,47,47,47,48,48,48,49,49,49,49,49,50,50,50,51,51,51,51,51,52,52,52,52,53,53,53,53,53,54,54,54,54,55,55,55,55,56,56,56,56,56,57,57,57,57,57,58,58,58,59,59,59,59,59,60,60,60,60,60,61,61,61,61,61,62,62,62,63,63,63,63,63,64,64,64,64,64,65,65,65,65,65,66,66,66,66,66,67,67,67,67,67,68,68,68,69,69,69,69,69,70,70,70,70,70,71,71,71,71,71,72,72,72,72,73,73,73,73,73,74,74,74,74,74,75,75,75,75,75,76,76,76,76,76,77,77,77,77,77,78,78,78,78,78,78,79,79,79,79,79,80,80,80,80,80,81,81,81,81,82,82,82,82,82,82,82,83,83,83,83,84,84,84,84,84,85,85,85,85,85,85,86,86,86,86,86,87,87,87,87,87,87,88,88,88,88,88,89,89,89,89,89,89,90,90,90,90,90,90,90,91,91,91,91,92,92,92,92,92,92,93,93,93,93,93,93,93,94,94,94,94,94,94,95,95,95,95,95,95,96,96,96,96,96,96,96,96,97,97,97,97,97,97,98,98,98,98,98,98,99,99,99,99,99,99,99,100,100,100,100,100,100,101,101,101,101,101,101,101,101,102,102,102,102,102,102,102,103,103,103,103,103,103,103,103,104,104,104,104,104,104,104,105,105,105,105,105,105,105,105,105,106,106,106,106,106,106,106,107,107,107,107,107,107,107,107,107,108,108,108,108,108,108,108,108,109,109,109,109,109,109,109,109,109,110,110,110,110,110,110,110,110,110,110,111,111,111,111,111,111,111,111,112,112,112,112,112,112,112,112,112,112,113,113,113,113,113,113,113,113,113,113,114,114,114,114,114,114,114,114,114,114,114,115,115,115,115,115,115,115,115,115,115,115,116,116,116,116,116,116,116,116,116,116,117,117,117,117,117,117,117,117,117,117,117,117,118,118,118,118,118,118,118,118,118,118,118,118,119,119,119,119,119,119,119,119,119,119,119,119,119,120,120,120,120,120,120,120,120,120,120,120,120,120,121,121,121,121,121,121,121,121,121,121,121,121,121,122,122,122,122,122,122,122,122,122,122,122,122,122,122,123,123,123,123,123,123,123,123,123,123,123,123,123,123,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,129,129,129,129,129,129,129,129,129,129,129,129,129,129,130,130,130,130,130,130,130,130,130,130,130,130,130,131,131,131,131,131,131,131,131,131,131,131,131,132,132,132,132,132,132,132,132,132,132,132,132,133,133,133,133,133,133,133,133,133,134,134,134,134,134,134,134,134,134,134,135,135,135,135,135,135,135,135,135,135,136,136,136,136,136,136,136,136,137,137,137,137,137,137,137,138,138,138,138,138,138,138,139,139,139,139,139,139,139,140,140,140,140,140,140,141,141,141,141,141,141,142,142,142,142,142,143,143,143,143,143,144,144,144,144,144,145,145,145,145,145,146,146,146,147,147,147,147,148,148,148,148,149,149,149,150,150,150,150,151,151,151,152,152,153,153,153,154,154,154,155,155,155,156,156,157,157,158,158,159,159,159,160,160,161,161,162,162,163,164,164,165,166,166,167,168,168,169,169,170,171,172,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,191,192,193,195,196,198,199,201,203,204,206,208,210,212,214,216,218,220,223,225,227,230,232,235,238,240,243,246,249,252").as_slice() + ); + assert_eq!( + brightness_contrast_map(0., -100.), + string_data("0,0,1,1,2,2,3,3,3,4,4,5,5,6,6,6,7,7,8,8,8,9,9,10,10,11,11,11,12,12,13,13,14,14,14,15,15,16,16,17,17,17,18,18,19,19,19,20,20,21,21,22,22,22,23,23,24,24,24,25,25,26,26,27,27,27,28,28,29,29,29,30,30,31,31,31,32,32,33,33,34,34,34,35,35,36,36,36,37,37,38,38,38,39,39,40,40,40,41,41,42,42,42,43,43,44,44,44,45,45,46,46,46,47,47,47,48,48,49,49,49,50,50,51,51,51,52,52,53,53,53,54,54,54,55,55,56,56,56,57,57,57,58,58,59,59,59,60,60,60,61,61,61,62,62,63,63,63,64,64,64,65,65,65,66,66,66,67,67,67,68,68,69,69,69,70,70,70,71,71,71,72,72,72,73,73,73,74,74,74,75,75,75,76,76,76,77,77,77,78,78,78,78,79,79,79,80,80,80,81,81,81,82,82,82,82,83,83,83,84,84,84,85,85,85,85,86,86,86,87,87,87,87,88,88,88,89,89,89,89,90,90,90,90,91,91,91,92,92,92,92,93,93,93,93,94,94,94,94,95,95,95,95,96,96,96,96,96,97,97,97,97,98,98,98,98,99,99,99,99,99,100,100,100,100,101,101,101,101,101,102,102,102,102,102,103,103,103,103,103,104,104,104,104,104,105,105,105,105,105,105,106,106,106,106,106,107,107,107,107,107,107,108,108,108,108,108,108,109,109,109,109,109,109,110,110,110,110,110,110,110,111,111,111,111,111,111,112,112,112,112,112,112,112,113,113,113,113,113,113,113,114,114,114,114,114,114,114,114,115,115,115,115,115,115,115,115,116,116,116,116,116,116,116,116,117,117,117,117,117,117,117,117,117,118,118,118,118,118,118,118,118,118,119,119,119,119,119,119,119,119,119,119,120,120,120,120,120,120,120,120,120,120,121,121,121,121,121,121,121,121,121,121,121,122,122,122,122,122,122,122,122,122,122,122,123,123,123,123,123,123,123,123,123,123,123,123,124,124,124,124,124,124,124,124,124,124,124,124,124,125,125,125,125,125,125,125,125,125,125,125,125,125,126,126,126,126,126,126,126,126,126,126,126,126,126,127,127,127,127,127,127,127,127,127,127,127,127,127,127,128,128,128,128,128,128,128,128,128,128,128,128,128,129,129,129,129,129,129,129,129,129,129,129,129,129,129,130,130,130,130,130,130,130,130,130,130,130,130,130,131,131,131,131,131,131,131,131,131,131,131,131,131,132,132,132,132,132,132,132,132,132,132,132,132,133,133,133,133,133,133,133,133,133,133,133,134,134,134,134,134,134,134,134,134,134,134,135,135,135,135,135,135,135,135,135,135,135,136,136,136,136,136,136,136,136,136,136,137,137,137,137,137,137,137,137,137,138,138,138,138,138,138,138,138,138,139,139,139,139,139,139,139,139,139,140,140,140,140,140,140,140,140,141,141,141,141,141,141,141,141,142,142,142,142,142,142,142,143,143,143,143,143,143,143,144,144,144,144,144,144,144,145,145,145,145,145,145,145,146,146,146,146,146,146,147,147,147,147,147,147,148,148,148,148,148,148,149,149,149,149,149,150,150,150,150,150,150,151,151,151,151,151,152,152,152,152,152,153,153,153,153,153,154,154,154,154,154,155,155,155,155,155,156,156,156,156,157,157,157,157,157,158,158,158,158,159,159,159,159,159,160,160,160,160,161,161,161,161,162,162,162,162,163,163,163,163,164,164,164,165,165,165,165,166,166,166,166,167,167,167,168,168,168,168,169,169,169,169,170,170,170,171,171,171,172,172,172,172,173,173,173,174,174,174,175,175,175,175,176,176,176,177,177,177,178,178,178,179,179,179,180,180,180,181,181,181,182,182,182,183,183,183,184,184,184,185,185,185,186,186,186,187,187,187,188,188,188,189,189,189,190,190,191,191,191,192,192,192,193,193,193,194,194,195,195,195,196,196,196,197,197,197,198,198,199,199,199,200,200,200,201,201,202,202,202,203,203,204,204,204,205,205,205,206,206,207,207,207,208,208,209,209,209,210,210,211,211,211,212,212,212,213,213,214,214,214,215,215,216,216,217,217,217,218,218,219,219,219,220,220,221,221,221,222,222,223,223,223,224,224,225,225,225,226,226,227,227,228,228,228,229,229,230,230,230,231,231,232,232,233,233,233,234,234,235,235,235,236,236,237,237,238,238,238,239,239,240,240,241,241,241,242,242,243,243,244,244,244,245,245,246,246,246,247,247,248,248,249,249,249,250,250,251,251,252,252,252,253,253,254,254,255,255").as_slice() + ); + assert_eq!( + brightness_contrast_map(53., -100.), + string_data("0,0,1,2,3,3,4,4,5,6,6,7,7,8,8,9,10,10,11,11,12,13,14,14,14,15,16,17,17,18,18,19,19,20,21,21,22,22,23,24,24,25,25,26,27,27,28,28,29,29,30,31,31,32,33,33,34,34,35,36,36,37,37,38,38,39,40,40,41,41,42,42,43,44,44,45,45,46,46,47,47,48,49,49,49,50,51,51,52,53,53,53,54,54,55,56,56,57,57,58,58,59,59,60,60,61,61,62,63,63,64,64,64,65,65,66,66,67,67,68,69,69,70,70,70,71,71,72,72,73,73,74,74,75,75,76,76,77,77,78,78,78,79,79,80,80,81,81,81,82,82,83,83,83,84,84,85,85,85,86,86,87,87,88,88,88,89,89,90,90,90,91,91,92,92,92,93,93,93,94,94,94,95,95,95,96,96,96,97,97,97,98,98,98,99,99,99,99,100,100,100,101,101,101,102,102,102,102,103,103,103,103,104,104,104,105,105,105,105,106,106,106,106,107,107,107,107,108,108,108,108,108,109,109,109,109,110,110,110,110,110,111,111,111,111,111,112,112,112,112,112,113,113,113,113,113,114,114,114,114,114,114,115,115,115,115,115,115,116,116,116,116,116,116,117,117,117,117,117,117,118,118,118,118,118,118,118,119,119,119,119,119,119,119,119,120,120,120,120,120,120,120,121,121,121,121,121,121,121,121,122,122,122,122,122,122,122,122,122,123,123,123,123,123,123,123,123,123,124,124,124,124,124,124,124,124,124,124,125,125,125,125,125,125,125,125,125,125,126,126,126,126,126,126,126,126,126,126,127,127,127,127,127,127,127,127,127,127,127,128,128,128,128,128,128,128,128,128,128,129,129,129,129,129,129,129,129,129,129,129,130,130,130,130,130,130,130,130,130,130,131,131,131,131,131,131,131,131,131,131,131,132,132,132,132,132,132,132,132,132,132,133,133,133,133,133,133,133,133,133,134,134,134,134,134,134,134,134,134,135,135,135,135,135,135,135,135,135,136,136,136,136,136,136,136,136,137,137,137,137,137,137,137,137,138,138,138,138,138,138,138,139,139,139,139,139,139,139,139,140,140,140,140,140,140,140,141,141,141,141,141,141,141,142,142,142,142,142,142,143,143,143,143,143,143,144,144,144,144,144,144,145,145,145,145,145,145,145,146,146,146,146,146,147,147,147,147,147,148,148,148,148,148,148,149,149,149,149,150,150,150,150,150,150,151,151,151,151,151,152,152,152,152,153,153,153,153,153,154,154,154,154,154,155,155,155,155,156,156,156,156,157,157,157,157,157,158,158,158,158,159,159,159,159,159,160,160,160,160,161,161,161,161,162,162,162,162,163,163,163,163,164,164,164,165,165,165,165,166,166,166,166,167,167,167,168,168,168,168,169,169,169,169,170,170,170,171,171,171,171,172,172,172,172,173,173,173,174,174,174,175,175,175,175,176,176,176,176,177,177,177,178,178,178,179,179,179,179,180,180,180,181,181,181,182,182,182,182,183,183,183,184,184,184,184,185,185,185,186,186,186,186,187,187,187,188,188,188,188,189,189,189,190,190,190,191,191,191,192,192,192,192,193,193,193,193,194,194,195,195,195,195,196,196,196,196,197,197,197,197,198,198,199,199,199,199,200,200,200,200,201,201,201,202,202,202,202,203,203,203,204,204,204,204,205,205,205,205,206,206,206,207,207,207,207,208,208,208,209,209,209,209,210,210,210,211,211,211,211,211,212,212,212,212,213,213,213,214,214,214,214,214,215,215,215,216,216,216,217,217,217,217,217,218,218,218,219,219,219,219,219,220,220,220,220,221,221,221,221,221,222,222,222,222,223,223,223,223,223,224,224,224,224,225,225,225,225,225,226,226,226,226,227,227,227,227,228,228,228,228,228,228,229,229,229,229,230,230,230,230,230,230,230,231,231,231,231,232,232,232,232,233,233,233,233,233,233,233,234,234,234,234,234,235,235,235,235,235,235,235,236,236,236,236,236,237,237,237,237,237,238,238,238,238,238,238,238,238,239,239,239,239,239,240,240,240,240,240,240,241,241,241,241,241,241,241,241,242,242,242,242,242,242,243,243,243,243,243,243,244,244,244,244,244,244,244,244,244,245,245,245,245,245,245,245,246,246,246,246,246,246,246,246,246,247,247,247,247,247,247,247,248,248,248,248,248,248,248,249,249,249,249,249,249,249,249,249,249,250,250,250,250,250,250,250,251,251,251,251,251,251,251,252,252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253,253,253,254,254,254,254,254,254,254,255,255,255,255,255,255").as_slice() + ); + assert_eq!( + brightness_contrast_map(150., -100.), + string_data("0,1,2,3,5,6,7,8,9,11,11,13,14,15,16,17,19,19,21,22,23,24,25,27,28,29,30,31,32,33,34,36,36,38,39,40,41,42,43,44,45,46,47,48,49,51,51,53,54,55,56,57,58,59,60,61,61,63,64,65,65,66,67,68,69,70,71,72,73,74,75,76,76,77,78,79,80,81,82,82,83,84,85,85,86,87,88,89,89,90,90,91,92,93,93,94,95,95,96,96,97,98,98,99,99,100,101,101,102,102,103,103,104,104,105,105,106,106,107,107,108,108,108,109,109,110,110,110,111,111,112,112,112,113,113,113,114,114,114,115,115,115,116,116,116,117,117,117,117,118,118,118,119,119,119,119,120,120,120,120,121,121,121,121,121,122,122,122,122,123,123,123,123,123,124,124,124,124,124,124,125,125,125,125,125,126,126,126,126,126,126,127,127,127,127,127,128,128,128,128,128,128,129,129,129,129,129,129,130,130,130,130,130,130,131,131,131,131,131,131,132,132,132,132,132,133,133,133,133,133,134,134,134,134,134,135,135,135,135,135,136,136,136,136,136,137,137,137,137,138,138,138,138,138,139,139,139,139,140,140,140,140,141,141,141,141,142,142,142,142,143,143,143,144,144,144,144,145,145,145,146,146,146,147,147,147,147,148,148,148,149,149,149,150,150,150,151,151,151,152,152,153,153,153,154,154,154,155,155,155,156,156,157,157,157,158,158,159,159,159,160,160,160,161,161,162,162,162,163,163,164,164,165,165,165,166,166,167,167,168,168,168,169,169,170,170,170,171,171,172,172,173,173,174,174,175,175,175,176,176,177,177,177,178,178,179,179,180,180,181,181,181,182,182,183,183,184,184,184,185,185,186,186,186,187,187,188,188,188,189,189,190,190,191,191,191,192,192,193,193,193,194,194,195,195,195,196,196,196,197,197,197,198,198,199,199,199,200,200,200,201,201,202,202,202,203,203,204,204,204,205,205,205,205,206,206,207,207,207,207,208,208,209,209,209,209,210,210,211,211,211,211,212,212,212,212,213,213,213,214,214,214,214,215,215,215,216,216,217,217,217,217,217,218,218,218,219,219,219,219,219,220,220,220,221,221,221,221,221,222,222,222,223,223,223,223,223,223,224,224,224,225,225,225,225,225,225,226,226,226,226,227,227,227,227,228,228,228,228,228,228,228,229,229,229,229,230,230,230,230,230,230,230,230,231,231,231,231,231,232,232,232,232,232,233,233,233,233,233,233,233,233,234,234,234,234,234,234,235,235,235,235,235,235,235,235,235,235,236,236,236,236,236,236,236,237,237,237,237,237,237,237,238,238,238,238,238,238,238,238,238,238,238,238,239,239,239,239,239,239,239,239,240,240,240,240,240,240,240,240,240,240,241,241,241,241,241,241,241,241,241,241,241,241,241,241,241,242,242,242,242,242,242,242,242,242,242,242,243,243,243,243,243,243,243,243,243,243,243,243,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,245,245,245,245,245,245,245,245,245,245,245,245,245,245,246,246,246,246,246,246,246,246,246,246,246,246,246,246,246,246,246,246,246,246,246,246,247,247,247,247,247,247,247,247,247,247,247,247,247,247,247,247,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255").as_slice() + ); + } +} diff --git a/node-graph/gstd/src/quantization.rs b/node-graph/gstd/src/quantization.rs index 19c735da..45da56e0 100644 --- a/node-graph/gstd/src/quantization.rs +++ b/node-graph/gstd/src/quantization.rs @@ -4,7 +4,7 @@ use graphene_core::raster::{Color, ImageFrame}; use graphene_core::Node; /// The `GenerateQuantizationNode` encodes the brightness of each channel of the image as an integer number -/// sepified by the samples parameter. This node is used to asses the loss of visual information when +/// signified by the samples parameter. This node is used to asses the loss of visual information when /// quantizing the image using different fit functions. pub struct GenerateQuantizationNode { samples: N, diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index 907b504e..4fe8d90d 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -248,7 +248,31 @@ fn node_registry() -> HashMap, params: [f64, f64, LuminanceCalculation]), raster_node!(graphene_core::raster::VibranceNode<_>, params: [f64]), - raster_node!(graphene_core::raster::BrightnessContrastNode< _, _>, params: [f64, f64]), + vec![( + NodeIdentifier::new("graphene_core::raster::BrightnessContrastNode<_, _, _>"), + |args| { + use graphene_core::raster::brightness_contrast::*; + + let brightness: DowncastBothNode<(), f64> = DowncastBothNode::new(args[0]); + let brightness = ClonedNode::new(brightness.eval(()) as f32); + let contrast: DowncastBothNode<(), f64> = DowncastBothNode::new(args[1]); + let contrast = ClonedNode::new(contrast.eval(()) as f32); + let use_legacy: DowncastBothNode<(), bool> = DowncastBothNode::new(args[2]); + + if use_legacy.eval(()) { + let generate_brightness_contrast_legacy_mapper_node = GenerateBrightnessContrastLegacyMapperNode::new(brightness, contrast); + let map_image_frame_node = graphene_std::raster::MapImageFrameNode::new(ValueNode::new(generate_brightness_contrast_legacy_mapper_node.eval(()))); + let any: DynAnyNode = graphene_std::any::DynAnyNode::new(ValueNode::new(map_image_frame_node)); + Box::pin(any) + } else { + let generate_brightness_contrast_mapper_node = GenerateBrightnessContrastMapperNode::new(brightness, contrast); + let map_image_frame_node = graphene_std::raster::MapImageFrameNode::new(ValueNode::new(generate_brightness_contrast_mapper_node.eval(()))); + let any: DynAnyNode = graphene_std::any::DynAnyNode::new(ValueNode::new(map_image_frame_node)); + Box::pin(any) + } + }, + NodeIOTypes::new(concrete!(ImageFrame), concrete!(ImageFrame), vec![value_fn!(f64), value_fn!(f64), value_fn!(bool)]), + )], raster_node!(graphene_core::raster::OpacityNode<_>, params: [f64]), raster_node!(graphene_core::raster::PosterizeNode<_>, params: [f64]), raster_node!(graphene_core::raster::ExposureNode<_, _, _>, params: [f64, f64, f64]),