diff --git a/imageflow_core/src/flow/definitions.rs b/imageflow_core/src/flow/definitions.rs index aceed4c0b..07d893432 100644 --- a/imageflow_core/src/flow/definitions.rs +++ b/imageflow_core/src/flow/definitions.rs @@ -523,6 +523,9 @@ impl From for Node { s::Node::RegionPercent { .. } => { Node::n(&nodes::REGION_PERCENT, NodeParams::Json(node)) } + s::Node::Region { .. } => { + Node::n(&nodes::REGION, NodeParams::Json(node)) + } s::Node::CopyRectToCanvas { .. } => { Node::n(&nodes::COPY_RECT, NodeParams::Json(node)) } diff --git a/imageflow_core/src/flow/nodes/clone_crop_fill_expand.rs b/imageflow_core/src/flow/nodes/clone_crop_fill_expand.rs index ab346b231..ee86bf464 100644 --- a/imageflow_core/src/flow/nodes/clone_crop_fill_expand.rs +++ b/imageflow_core/src/flow/nodes/clone_crop_fill_expand.rs @@ -9,6 +9,7 @@ pub static CLONE: CloneDef = CloneDef{}; pub static EXPAND_CANVAS: ExpandCanvasDef = ExpandCanvasDef{}; pub static CROP_WHITESPACE: CropWhitespaceDef = CropWhitespaceDef{}; pub static REGION_PERCENT: RegionPercentDef = RegionPercentDef {}; +pub static REGION: RegionDef = RegionDef {}; #[derive(Debug, Clone)] pub struct CopyRectNodeDef; @@ -232,10 +233,10 @@ pub struct RegionPercentDef; impl RegionPercentDef { fn get_coords(info:FrameInfo, left: f32, top: f32, right: f32, bottom: f32) -> (i32,i32,i32,i32){ let (x1, y1, mut x2, mut y2) = - ((info.w as f32 * left).round() as i32, - (info.h as f32 * top).round() as i32, - (info.w as f32 * right).round() as i32, - (info.h as f32 * bottom).round() as i32); + ((info.w as f32 * left / 100f32).round() as i32, + (info.h as f32 * top / 100f32).round() as i32, + (info.w as f32 * right / 100f32).round() as i32, + (info.h as f32 * bottom / 100f32).round() as i32); //Round up to 1px if our percentages land on the same pixel if x2 < x1 { x2 = x1 + 1; @@ -243,6 +244,7 @@ impl RegionPercentDef { if y2 < y1{ y2 = y1 + 1; } + //eprintln!("{}x{} {},{},{},{} -> {},{},{},{}", info.w, info.h, left, top, right, bottom, x1,y1,x2,y2); return (x1,y1,x2,y2); } @@ -253,13 +255,13 @@ impl NodeDef for RegionPercentDef { } } impl NodeDefOneInputExpand for RegionPercentDef { - fn fqn(&self) -> &'static str{ + fn fqn(&self) -> &'static str { "imazen.region_percent" } fn estimate(&self, p: &NodeParams, input: FrameEstimate) -> Result { - if let NodeParams::Json(s::Node::RegionPercent { left, top, bottom, right, ref color }) = *p { - input.map_frame( |info| { - let (x1, y1, x2, y2) = RegionPercentDef::get_coords(info, left, top, right, bottom); + if let NodeParams::Json(imageflow_types::Node::RegionPercent { x1, y1, x2, y2, ref background_color }) = *p { + input.map_frame(|info| { + let (x1, y1, x2, y2) = RegionPercentDef::get_coords(info, x1, y1, x2, y2); Ok(FrameInfo { w: x2 - x1, h: y2 - y1, @@ -267,18 +269,73 @@ impl NodeDefOneInputExpand for RegionPercentDef { }) }) } else { - Err(nerror!(crate::ErrorKind::NodeParamsMismatch, "Need ExpandCanvas, got {:?}", p)) + Err(nerror!(crate::ErrorKind::NodeParamsMismatch, "Need RegionPercent, got {:?}", p)) } } //TODO: If we want to support transparency on jpeg inputs we have to fix expand_canvas and copy_rect too - fn expand(&self, ctx: &mut OpCtxMut, ix: NodeIndex, p: NodeParams, input: FrameInfo) -> Result<()>{ - if let NodeParams::Json(imageflow_types::Node::RegionPercent { left, top, bottom, right, color }) = p { + fn expand(&self, ctx: &mut OpCtxMut, ix: NodeIndex, p: NodeParams, input: FrameInfo) -> Result<()> { + if let NodeParams::Json(imageflow_types::Node::RegionPercent { x1: left, y1: top, y2: bottom, x2: right, background_color }) = p { if bottom <= top || right <= left { return Err(nerror!(crate::ErrorKind::InvalidNodeParams, "Invalid coordinates: {},{} {},{} should describe the top-left and bottom-right corners of the region in percentages. Not a rectangle.", left, top, right, bottom)); } let (x1, y1, x2, y2) = RegionPercentDef::get_coords(input, left, top, right, bottom); + let region_params = imageflow_types::Node::Region { + x1, + y1, + x2, + y2, + background_color: background_color.clone() + }; + + //First crop, then expand + ctx.replace_node(ix, vec![ + Node::n(®ION, + NodeParams::Json(region_params)), + ]); + + + Ok(()) + } else { + Err(nerror!(crate::ErrorKind::NodeParamsMismatch, "Need RegionPercent, got {:?}", p)) + } + } +} + +#[derive(Debug,Clone)] +pub struct RegionDef; +impl NodeDef for RegionDef { + fn as_one_input_expand(&self) -> Option<&dyn NodeDefOneInputExpand>{ + Some(self) + } +} +impl NodeDefOneInputExpand for RegionDef { + fn fqn(&self) -> &'static str{ + "imazen.region" + } + fn estimate(&self, p: &NodeParams, input: FrameEstimate) -> Result { + if let NodeParams::Json(imageflow_types::Node::Region { x1, y1, x2, y2, ref background_color }) = *p { + input.map_frame( |info| { + if y2 <= y1 || x2 <= x1 { + return Err(nerror!(crate::ErrorKind::InvalidNodeParams, "Invalid coordinates: {},{} {},{} should describe the top-left and bottom-right corners of the region in pixels. Not a rectangle.", x1, y1, x2, y2)); + } + Ok(FrameInfo { + w: x2 - x1, + h: y2 - y1, + fmt: ffi::PixelFormat::from(info.fmt) + }) + }) + } else { + Err(nerror!(crate::ErrorKind::NodeParamsMismatch, "Need Region, got {:?}", p)) + } + } + //TODO: If we want to support transparency on jpeg inputs we have to fix expand_canvas and copy_rect too + fn expand(&self, ctx: &mut OpCtxMut, ix: NodeIndex, p: NodeParams, input: FrameInfo) -> Result<()>{ + if let NodeParams::Json(imageflow_types::Node::Region { x1, y1, y2, x2, background_color }) = p { + if y2 <= y1 || x2 <= x1 { + return Err(nerror!(crate::ErrorKind::InvalidNodeParams, "Invalid coordinates: {},{} {},{} should describe the top-left and bottom-righ corners of the region in pixels. Not a rectangle.", x1, y1, x2, y2)); + } if x1 >= input.w || y1 >= input.h || x2 <= 0 || y2 <= 0{ // No cropping of input image, we just create a canvas @@ -286,12 +343,14 @@ impl NodeDefOneInputExpand for RegionPercentDef { w: (x2-x1) as usize, h: (y2-y1) as usize, format: s::PixelFormat::from(input.fmt), - color: color.clone(), + color: background_color.clone(), }; let canvas = ctx.graph .add_node(Node::n(&CREATE_CANVAS, NodeParams::Json(canvas_params))); - ctx.replace_node_with_existing(ix, canvas); + + ctx.copy_edges_to(ix, canvas, EdgeDirection::Outgoing); + ctx.graph.remove_node(ix).unwrap(); }else{ let crop_params = imageflow_types::Node::Crop { x1: i32::min(input.w, i32::max(0,x1)) as u32, @@ -300,11 +359,11 @@ impl NodeDefOneInputExpand for RegionPercentDef { y2: i32::min(input.h, i32::max(0,y2)) as u32 }; let expand_params = imageflow_types::Node::ExpandCanvas { - left: (i32::max(0, x1) * -1) as u32, - top: (i32::max(0, y1) * -1) as u32, - right: i32::min(0, x2 - input.w) as u32, - bottom: i32::min(0, y2 - input.h) as u32, - color: color.clone() + left: i32::max(0, 0 - x1) as u32, + top: i32::max(0, 0 - y1) as u32, + right: i32::max(0, x2 - input.w) as u32, + bottom: i32::max(0, y2 - input.h) as u32, + color: background_color.clone() }; //First crop, then expand @@ -318,11 +377,10 @@ impl NodeDefOneInputExpand for RegionPercentDef { Ok(()) } else { - Err(nerror!(crate::ErrorKind::NodeParamsMismatch, "Need RegionPercentage, got {:?}", p)) + Err(nerror!(crate::ErrorKind::NodeParamsMismatch, "Need Region, got {:?}", p)) } } } - #[derive(Debug, Clone)] pub struct CropMutNodeDef; diff --git a/imageflow_core/src/flow/nodes/mod.rs b/imageflow_core/src/flow/nodes/mod.rs index fe53f2a87..d16709f62 100644 --- a/imageflow_core/src/flow/nodes/mod.rs +++ b/imageflow_core/src/flow/nodes/mod.rs @@ -36,6 +36,7 @@ pub use self::clone_crop_fill_expand::CROP_WHITESPACE; pub use self::clone_crop_fill_expand::CROP_MUTATE; pub use self::clone_crop_fill_expand::EXPAND_CANVAS; pub use self::clone_crop_fill_expand::REGION_PERCENT; +pub use self::clone_crop_fill_expand::REGION; pub use self::clone_crop_fill_expand::FILL_RECT; pub use self::codecs_and_pointer::BITMAP_BGRA_POINTER; pub use self::codecs_and_pointer::DECODER; diff --git a/imageflow_core/tests/visuals.rs b/imageflow_core/tests/visuals.rs index bf692d6b3..c011b76cd 100644 --- a/imageflow_core/tests/visuals.rs +++ b/imageflow_core/tests/visuals.rs @@ -74,7 +74,7 @@ fn test_expand_rect(){ #[test] fn test_crop(){ - for _ in 1..100 { + for _ in 1..100 { //WTF are we looping 100 times for? let matched = compare(None, 500, "FillRectAndCrop", POPULATE_CHECKSUMS, DEBUG_GRAPH, vec![ s::Node::CreateCanvas { w: 200, h: 200, format: s::PixelFormat::Bgra32, color: s::Color::Srgb(s::ColorSrgb::Hex("FF5555FF".to_owned())) }, @@ -86,6 +86,45 @@ fn test_crop(){ } } +#[test] +fn test_off_surface_region(){ + + let matched = compare(None, 500, + "TestOffSurfaceRegion", POPULATE_CHECKSUMS, DEBUG_GRAPH, vec![ + s::Node::CreateCanvas { w: 200, h: 200, format: s::PixelFormat::Bgra32, color: s::Color::Srgb(s::ColorSrgb::Hex("FF5555FF".to_owned())) }, + s::Node::FillRect { x1: 0, y1: 0, x2: 10, y2: 100, color: s::Color::Srgb(s::ColorSrgb::Hex("0000FFFF".to_owned())) }, + s::Node::RegionPercent { x1: -100f32, y1: -100f32, x2: -1f32, y2: -1f32, background_color: s::Color::Transparent} + ] + ); + assert!(matched); + +} +#[test] +fn test_partial_region(){ + + let matched = compare(None, 500, + "TestPartialRegion", POPULATE_CHECKSUMS, DEBUG_GRAPH, vec![ + s::Node::CreateCanvas { w: 200, h: 200, format: s::PixelFormat::Bgra32, color: s::Color::Srgb(s::ColorSrgb::Hex("FF5555FF".to_owned())) }, + s::Node::FillRect { x1: 0, y1: 0, x2: 10, y2: 100, color: s::Color::Srgb(s::ColorSrgb::Hex("0000FFFF".to_owned())) }, + s::Node::RegionPercent { x1: -10f32, y1: -10f32, x2: 40f32, y2: 40f32, background_color: s::Color::Transparent} + ] + ); + assert!(matched); + +} +#[test] +fn test_pixels_region(){ + + let matched = compare(None, 500, + "TestPixelsRegion", POPULATE_CHECKSUMS, DEBUG_GRAPH, vec![ + s::Node::CreateCanvas { w: 200, h: 200, format: s::PixelFormat::Bgra32, color: s::Color::Srgb(s::ColorSrgb::Hex("FF5555FF".to_owned())) }, + s::Node::FillRect { x1: 0, y1: 0, x2: 10, y2: 100, color: s::Color::Srgb(s::ColorSrgb::Hex("0000FFFF".to_owned())) }, + s::Node::Region { x1: -10, y1: -10, x2: 120, y2: 50, background_color: s::Color::Transparent} + ] + ); + assert!(matched); + +} // Replaces TEST_CASE("Test scale rings", "") diff --git a/imageflow_core/tests/visuals/checksums.json b/imageflow_core/tests/visuals/checksums.json index 390215c81..ff17608d5 100644 --- a/imageflow_core/tests/visuals/checksums.json +++ b/imageflow_core/tests/visuals/checksums.json @@ -11,6 +11,9 @@ "ScaleIDCTFastvsSlow": "04549C40B272C69E8_0EED88CCC2F4CD12F", "ScaleIDCT_approx_gamma": "01DD6D368FDF435EF_0E685FA15F0460A97", "ScaleTheHouse": "047AE058719BD4B9C_0EED88CCC2F4CD12F", + "TestOffSurfaceRegion": "09FA1A44E855DFBB0_01D9D2C49A4E782F9", + "TestPartialRegion": "0C51C8FBFD7A960E5_0172196390B512E97", + "TestPixelsRegion": "0B42E995F9EF3B17E_0AB6E6EE83BE6CCAF", "Test_Apply_Orientation_Landscape_1.jpg": "0DD4E589FF3B2583D_0AF9F040B874D83F7", "Test_Apply_Orientation_Landscape_2.jpg": "0002595D4CEF82803_0AF9F040B874D83F7", "Test_Apply_Orientation_Landscape_3.jpg": "0BE76E2D685843DBE_0AF9F040B874D83F7", diff --git a/imageflow_types/src/lib.rs b/imageflow_types/src/lib.rs index f0270163d..3a8134538 100644 --- a/imageflow_types/src/lib.rs +++ b/imageflow_types/src/lib.rs @@ -509,11 +509,19 @@ pub enum Node { }, #[serde(rename="region_percent")] RegionPercent { - left: f32, - top: f32, - right: f32, - bottom: f32, - color: Color, + x1: f32, + y1: f32, + x2: f32, + y2: f32, + background_color: Color, + }, + #[serde(rename="region")] + Region { + x1: i32, + y1: i32, + x2: i32, + y2: i32, + background_color: Color, }, #[serde(rename="transpose")] Transpose,