From 696fea2036d3439dd6969570caef9b87cbc76db9 Mon Sep 17 00:00:00 2001 From: Lilith River Date: Fri, 3 Jul 2020 18:21:52 -0600 Subject: [PATCH] Fix bug where &crop=x1,y1,x2,y2 values are not adjusted for decoder pre-shrinking --- imageflow_core/src/clients/stateless.rs | 2 +- imageflow_core/src/codecs/gif/mod.rs | 6 +- imageflow_core/src/codecs/jpeg_decoder.rs | 7 +- imageflow_core/src/codecs/libpng_decoder.rs | 6 +- imageflow_core/src/codecs/mod.rs | 3 +- imageflow_core/src/codecs/mozjpeg_decoder.rs | 19 ++++- imageflow_core/src/codecs/webp.rs | 42 +++++++--- imageflow_core/src/context.rs | 8 +- imageflow_core/src/context_methods.rs | 8 +- .../src/flow/nodes/codecs_and_pointer.rs | 2 +- imageflow_core/src/flow/nodes/constrain.rs | 20 ++++- imageflow_core/src/flow/nodes/watermark.rs | 2 +- imageflow_core/tests/common/mod.rs | 2 +- imageflow_core/tests/visuals.rs | 16 ++++ imageflow_core/tests/visuals/checksums.json | 1 + imageflow_riapi/src/ir4/layout.rs | 77 ++++++++++++++++--- imageflow_riapi/src/ir4/mod.rs | 24 ++++-- 17 files changed, 194 insertions(+), 51 deletions(-) diff --git a/imageflow_core/src/clients/stateless.rs b/imageflow_core/src/clients/stateless.rs index f67b0ac6a..74e949253 100644 --- a/imageflow_core/src/clients/stateless.rs +++ b/imageflow_core/src/clients/stateless.rs @@ -83,7 +83,7 @@ impl LibClient { fn get_image_info_inner(context: &mut Context, bytes: &[u8]) -> std::result::Result { context.add_input_bytes(0, bytes).map_err(|e| e.at(here!()))?; - Ok(context.get_image_info(0).map_err(|e| e.at(here!()))?) + Ok(context.get_unscaled_image_info(0).map_err(|e| e.at(here!()))?) } pub fn get_image_info(&mut self, bytes: &[u8]) diff --git a/imageflow_core/src/codecs/gif/mod.rs b/imageflow_core/src/codecs/gif/mod.rs index e8d29f216..2a7c33de4 100644 --- a/imageflow_core/src/codecs/gif/mod.rs +++ b/imageflow_core/src/codecs/gif/mod.rs @@ -92,8 +92,10 @@ impl Decoder for GifDecoder { Ok(()) } - - fn get_image_info(&mut self, c: &Context) -> Result { + fn get_scaled_image_info(&mut self, c: &Context) -> Result{ + self.get_unscaled_image_info(c) + } + fn get_unscaled_image_info(&mut self, c: &Context) -> Result { Ok(s::ImageInfo { frame_decodes_into: s::PixelFormat::Bgra32, image_width: i32::from(self.reader.width()), diff --git a/imageflow_core/src/codecs/jpeg_decoder.rs b/imageflow_core/src/codecs/jpeg_decoder.rs index 5b237c670..bc45caf68 100644 --- a/imageflow_core/src/codecs/jpeg_decoder.rs +++ b/imageflow_core/src/codecs/jpeg_decoder.rs @@ -45,8 +45,11 @@ impl Decoder for JpegDecoder { Ok(()) } + fn get_scaled_image_info(&mut self, c: &Context) -> Result{ + self.get_unscaled_image_info(c) + } - fn get_image_info(&mut self, c: &Context) -> Result { + fn get_unscaled_image_info(&mut self, c: &Context) -> Result { self.decoder.read_info()?; let info = self.decoder.info().expect("error handling not yet implemented for jpeg"); @@ -77,7 +80,7 @@ impl Decoder for JpegDecoder { fn read_frame(&mut self, c: &Context) -> Result<*mut BitmapBgra> { if self.width.is_none() { - let _ = self.get_image_info(c)?; + let _ = self.get_scaled_image_info(c)?; } let pixels = self.decoder.decode()?; diff --git a/imageflow_core/src/codecs/libpng_decoder.rs b/imageflow_core/src/codecs/libpng_decoder.rs index f467b8e3b..eb438fb54 100644 --- a/imageflow_core/src/codecs/libpng_decoder.rs +++ b/imageflow_core/src/codecs/libpng_decoder.rs @@ -38,7 +38,11 @@ impl Decoder for LibPngDecoder { Ok(()) } - fn get_image_info(&mut self, c: &Context) -> Result { + fn get_scaled_image_info(&mut self, c: &Context) -> Result{ + self.get_unscaled_image_info(c) + } + + fn get_unscaled_image_info(&mut self, c: &Context) -> Result { let (w,h,fmt) = self.decoder.get_info()?; Ok(s::ImageInfo { diff --git a/imageflow_core/src/codecs/mod.rs b/imageflow_core/src/codecs/mod.rs index 7e5e59c5c..c539902de 100644 --- a/imageflow_core/src/codecs/mod.rs +++ b/imageflow_core/src/codecs/mod.rs @@ -36,7 +36,8 @@ pub trait DecoderFactory{ } pub trait Decoder : Any{ fn initialize(&mut self, c: &Context) -> Result<()>; - fn get_image_info(&mut self, c: &Context) -> Result; + fn get_unscaled_image_info(&mut self, c: &Context) -> Result; + fn get_scaled_image_info(&mut self, c: &Context) -> Result; fn get_exif_rotation_flag(&mut self, c: &Context) -> Result>; fn tell_decoder(&mut self, c: &Context, tell: s::DecoderCommand) -> Result<()>; fn read_frame(&mut self, c: &Context) -> Result<*mut BitmapBgra>; diff --git a/imageflow_core/src/codecs/mozjpeg_decoder.rs b/imageflow_core/src/codecs/mozjpeg_decoder.rs index 132aa7ce9..3bc716e97 100644 --- a/imageflow_core/src/codecs/mozjpeg_decoder.rs +++ b/imageflow_core/src/codecs/mozjpeg_decoder.rs @@ -40,7 +40,20 @@ impl Decoder for MozJpegDecoder { Ok(()) } - fn get_image_info(&mut self, c: &Context) -> Result { + + fn get_unscaled_image_info(&mut self, c: &Context) -> Result { + let (w,h) = self.decoder.get_original_size()?; + + Ok(s::ImageInfo { + frame_decodes_into: s::PixelFormat::Bgr32, + image_width: w as i32, + image_height: h as i32, + preferred_mime_type: "image/jpeg".to_owned(), + preferred_extension: "jpg".to_owned() + }) + } + + fn get_scaled_image_info(&mut self, c: &Context) -> Result { let (w,h) = self.decoder.get_final_size()?; Ok(s::ImageInfo { @@ -231,6 +244,10 @@ impl MzDec{ self.apply_downscaling(); Ok((self.w, self.h)) } + fn get_original_size(&mut self) -> Result<(u32,u32)>{ + self.read_header()?; + Ok((self.original_width, self.original_height)) + } fn get_exif_rotation_flag(&mut self) -> Result>{ self.read_header()?; diff --git a/imageflow_core/src/codecs/webp.rs b/imageflow_core/src/codecs/webp.rs index 94530042c..2e7ce4022 100644 --- a/imageflow_core/src/codecs/webp.rs +++ b/imageflow_core/src/codecs/webp.rs @@ -36,7 +36,7 @@ impl WebPDecoder { }) } - pub fn ensure_data_buffered(&mut self, c: &Context) -> Result<()>{ + fn ensure_data_buffered(&mut self) -> Result<()>{ if self.bytes.is_none() { let mut bytes = Vec::with_capacity(2048); let _ = self.io.read_to_end(&mut bytes).map_err(|e| FlowError::from_decoder(e)); @@ -73,17 +73,9 @@ impl WebPDecoder { self.input_height() } } -} - - -impl Decoder for WebPDecoder { - fn initialize(&mut self, c: &Context) -> Result<()> { - Ok(()) - } - - fn get_image_info(&mut self, c: &Context) -> Result { - self.ensure_data_buffered(c)?; + fn ensure_features_read(&mut self) -> Result<()>{ + self.ensure_data_buffered()?; if !self.features_read { let buf = &self.bytes.as_ref().unwrap(); //Cannot fail after ensure_data_buffered let len = buf.len(); @@ -95,6 +87,32 @@ impl Decoder for WebPDecoder { } self.features_read = true; } + Ok(()) + } +} + + +impl Decoder for WebPDecoder { + fn initialize(&mut self, c: &Context) -> Result<()> { + Ok(()) + } + + + fn get_scaled_image_info(&mut self, c: &Context) -> Result { + self.ensure_features_read()?; + + Ok(s::ImageInfo { + frame_decodes_into: s::PixelFormat::Bgra32, + image_width: self.output_width().unwrap(), + image_height: self.output_height().unwrap(), + preferred_mime_type: "image/webp".to_owned(), + preferred_extension: "webp".to_owned() + }) + } + + fn get_unscaled_image_info(&mut self, c: &Context) -> Result { + self.ensure_features_read()?; + Ok(s::ImageInfo { frame_decodes_into: s::PixelFormat::Bgra32, image_width: self.input_width().unwrap(), @@ -119,7 +137,7 @@ impl Decoder for WebPDecoder { } fn read_frame(&mut self, c: &Context) -> Result<*mut BitmapBgra> { - let _ = self.get_image_info(c)?; + let _ = self.get_scaled_image_info(c)?; unsafe { let w = self.output_width().unwrap(); diff --git a/imageflow_core/src/context.rs b/imageflow_core/src/context.rs index 809a02b02..7a6d8e1dc 100644 --- a/imageflow_core/src/context.rs +++ b/imageflow_core/src/context.rs @@ -181,10 +181,12 @@ impl Context { } - pub fn get_image_info(&mut self, io_id: i32) -> Result { - self.get_codec(io_id).map_err(|e| e.at(here!()))?.get_decoder().map_err(|e| e.at(here!()))?.get_image_info(self).map_err(|e| e.at(here!())) + pub fn get_unscaled_image_info(&mut self, io_id: i32) -> Result { + self.get_codec(io_id).map_err(|e| e.at(here!()))?.get_decoder().map_err(|e| e.at(here!()))?.get_unscaled_image_info(self).map_err(|e| e.at(here!())) + } + pub fn get_scaled_image_info(&mut self, io_id: i32) -> Result { + self.get_codec(io_id).map_err(|e| e.at(here!()))?.get_decoder().map_err(|e| e.at(here!()))?.get_scaled_image_info(self).map_err(|e| e.at(here!())) } - pub fn tell_decoder(&mut self, io_id: i32, tell: s::DecoderCommand) -> Result<()> { self.get_codec(io_id).map_err(|e| e.at(here!()))?.get_decoder().map_err(|e| e.at(here!()))?.tell_decoder(self, tell).map_err(|e| e.at(here!())) } diff --git a/imageflow_core/src/context_methods.rs b/imageflow_core/src/context_methods.rs index 52482553d..b698b5b3d 100644 --- a/imageflow_core/src/context_methods.rs +++ b/imageflow_core/src/context_methods.rs @@ -25,11 +25,15 @@ fn create_context_router() -> MethodRouter<'static, Context> { })); r.add_responder("v0.1/get_image_info", Box::new(move |context: &mut Context, data: s::GetImageInfo001| { - Ok(s::ResponsePayload::ImageInfo(context.get_image_info(data.io_id).map_err(|e| e.at(here!()))?)) + Ok(s::ResponsePayload::ImageInfo(context.get_unscaled_image_info(data.io_id).map_err(|e| e.at(here!()))?)) })); r.add_responder("v1/get_image_info", Box::new(move |context: &mut Context, data: s::GetImageInfo001| { - Ok(s::ResponsePayload::ImageInfo(context.get_image_info(data.io_id).map_err(|e| e.at(here!()))?)) + Ok(s::ResponsePayload::ImageInfo(context.get_unscaled_image_info(data.io_id).map_err(|e| e.at(here!()))?)) + })); + r.add_responder("v1/get_scaled_image_info", + Box::new(move |context: &mut Context, data: s::GetImageInfo001| { + Ok(s::ResponsePayload::ImageInfo(context.get_scaled_image_info(data.io_id).map_err(|e| e.at(here!()))?)) })); r.add_responder("v0.1/tell_decoder", Box::new(move |context: &mut Context, data: s::TellDecoder001| { diff --git a/imageflow_core/src/flow/nodes/codecs_and_pointer.rs b/imageflow_core/src/flow/nodes/codecs_and_pointer.rs index 31ea77cd0..6ef41b26b 100644 --- a/imageflow_core/src/flow/nodes/codecs_and_pointer.rs +++ b/imageflow_core/src/flow/nodes/codecs_and_pointer.rs @@ -92,7 +92,7 @@ fn decoder_get_io_id(params: &NodeParams) -> Result { } fn decoder_estimate(ctx: &mut OpCtxMut, ix: NodeIndex) -> Result { let io_id = decoder_get_io_id(&ctx.weight(ix).params).map_err(|e| e.at(here!()))?; - let frame_info = ctx.job.get_image_info(io_id).map_err(|e| e.at(here!()))?; + let frame_info = ctx.job.get_scaled_image_info(io_id).map_err(|e| e.at(here!()))?; Ok(FrameEstimate::Some(FrameInfo { fmt: frame_info.frame_decodes_into, diff --git a/imageflow_core/src/flow/nodes/constrain.rs b/imageflow_core/src/flow/nodes/constrain.rs index ca018ce4c..8e435a9f6 100644 --- a/imageflow_core/src/flow/nodes/constrain.rs +++ b/imageflow_core/src/flow/nodes/constrain.rs @@ -1,6 +1,6 @@ use super::internal_prelude::*; use imageflow_riapi::sizing::{Layout, AspectRatio, LayoutError}; -use imageflow_types::ConstraintMode; +use imageflow_types::{ConstraintMode, ImageInfo}; pub static CONSTRAIN: ConstrainDef = ConstrainDef{}; pub static COMMAND_STRING: CommandStringDef = CommandStringDef{}; @@ -12,7 +12,7 @@ pub static EXPANDING_COMMAND_STRING: CommandStringPartiallyExpandedDef = Command fn get_decoder_mime(ctx: &mut OpCtxMut, ix: NodeIndex) -> Result>{ let decoders = ctx.get_decoder_io_ids_and_indexes(ix); if let Some((io_id, _)) = decoders.first(){ - Ok(Some(ctx.c.get_codec(*io_id)?.get_decoder()?.get_image_info(ctx.c)?.preferred_mime_type)) + Ok(Some(ctx.job.get_unscaled_image_info(*io_id)?.preferred_mime_type)) } else{ Ok(None) @@ -22,6 +22,15 @@ fn get_decoder_mime(ctx: &mut OpCtxMut, ix: NodeIndex) -> Result> fn get_expand(ctx: &mut OpCtxMut, ix: NodeIndex) -> Result<::imageflow_riapi::ir4::Ir4Expand>{ let input = ctx.first_parent_frame_info_some(ix).ok_or_else(|| nerror!(crate::ErrorKind::InvalidNodeConnections, "CommandString node requires that its parent nodes be perfectly estimable"))?; + + let mut image_info: Option = None; + if let Some((io_id, decoder_ix)) = ctx.get_decoder_io_ids_and_indexes(ix).first(){ + image_info = Some(ctx.job.get_unscaled_image_info(*io_id).map_err(|e| e.at(here!()))?); + + } + + + let params = &ctx.weight(ix).params; if let NodeParams::Json(s::Node::CommandString{ref kind, ref value, ref decode, ref encode, ref watermarks}) = *params { @@ -36,7 +45,10 @@ fn get_expand(ctx: &mut OpCtxMut, ix: NodeIndex) -> Result<::imageflow_riapi::ir h: input.h, fmt: input.fmt, original_mime: get_decoder_mime(ctx,ix)? - } + }, + reference_width: image_info.as_ref().map(|i| i.image_width).unwrap_or(input.w), + reference_height: image_info.as_ref().map(|i| i.image_height).unwrap_or(input.h), + }) } } @@ -206,7 +218,7 @@ impl NodeDef for CommandStringDef{ decode_id: Some(d_id), encode_id: None, watermarks, - }.get_decode_node().unwrap(); + }.get_decode_node_without_commands().unwrap(); ctx.replace_node(ix, vec![ Node::from(decode_node), Node::n(&EXPANDING_COMMAND_STRING, params) diff --git a/imageflow_core/src/flow/nodes/watermark.rs b/imageflow_core/src/flow/nodes/watermark.rs index b09eaa303..222fdd9c6 100644 --- a/imageflow_core/src/flow/nodes/watermark.rs +++ b/imageflow_core/src/flow/nodes/watermark.rs @@ -87,7 +87,7 @@ impl NodeDefOneInputExpand for WatermarkDef { canvas_color: None }; - let watermark_info = ctx.job.get_image_info(watermark.io_id).map_err(|e| e.at(here!()))?; + let watermark_info = ctx.job.get_scaled_image_info(watermark.io_id).map_err(|e| e.at(here!()))?; let constraint_results = imageflow_riapi::ir4::process_constraint(watermark_info.image_width, watermark_info.image_height, &constraint).unwrap(); //TODO: fix unwrap diff --git a/imageflow_core/tests/common/mod.rs b/imageflow_core/tests/common/mod.rs index dd4a4d860..7a81f4306 100644 --- a/imageflow_core/tests/common/mod.rs +++ b/imageflow_core/tests/common/mod.rs @@ -752,7 +752,7 @@ pub fn test_with_callback(checksum_name: &str, input: IoTestEnum, callback: fn(& unsafe { IoTestTranslator{}.add(&mut context, 0, input).unwrap(); - let image_info = context.get_image_info(0).unwrap(); + let image_info = context.get_unscaled_image_info(0).unwrap(); let (tell_decoder, mut steps): (Option, Vec) = callback(&image_info); diff --git a/imageflow_core/tests/visuals.rs b/imageflow_core/tests/visuals.rs index 5fd267ba0..8895121ad 100644 --- a/imageflow_core/tests/visuals.rs +++ b/imageflow_core/tests/visuals.rs @@ -486,6 +486,22 @@ fn decode_cmyk_jpeg() { } +#[test] +fn test_crop_with_preshrink() { + let matched = compare(Some(IoTestEnum::Url("https://resizer-images.s3.amazonaws.com/private/cropissue.jpg".to_owned())), 500, + "crop_with_preshrink", POPULATE_CHECKSUMS, DEBUG_GRAPH, vec![ + Node::CommandString{ + kind: CommandStringKind::ImageResizer4, + value: "w=170&h=220&mode=crop&scale=both&crop=449,0,-472,0".to_owned(), + decode: Some(0), + encode: None, + watermarks: None + } + ] + ); + assert!(matched); + +} #[test] fn webp_lossless_alpha_decode_and_scale() { diff --git a/imageflow_core/tests/visuals/checksums.json b/imageflow_core/tests/visuals/checksums.json index 9c7f1607b..fa57c0c5b 100644 --- a/imageflow_core/tests/visuals/checksums.json +++ b/imageflow_core/tests/visuals/checksums.json @@ -35,6 +35,7 @@ "WatermarkSmall": "09BF9877BE2CB36AC_0DE3336C466C67DD9", "WhiteBalanceNight": "0D84206BD2DD308DA_05C9231C6124D62A8", "cmyk_decode": "00C5CD1E1ADF2628A_0D58EB335590501F3", + "crop_with_preshrink": "04FF87FFA9A645517_09D5B46DC8E13B7B4", "encode_frymire": "07FC2C05A26B49BF1.png", "encode_gradients": "00D10466A8552DCF6.png", "encode_marsRGB": "07800DDF0E598E7E4.png", diff --git a/imageflow_riapi/src/ir4/layout.rs b/imageflow_riapi/src/ir4/layout.rs index 5b0076962..03d2126a9 100644 --- a/imageflow_riapi/src/ir4/layout.rs +++ b/imageflow_riapi/src/ir4/layout.rs @@ -9,6 +9,8 @@ use imageflow_types::{ConstraintMode, ConstraintGravity, WatermarkConstraintBox} pub struct Ir4Layout{ i: Instructions, + reference_width: i32, + reference_height: i32, /// source width w: i32, h: i32 @@ -28,11 +30,12 @@ pub struct Ir4LayoutInfo{ impl Ir4Layout{ pub fn new(i: Instructions, - - w: i32, - h: i32) -> Ir4Layout{ + w: i32, + h: i32, + reference_width: i32, + reference_height: i32) -> Ir4Layout{ Ir4Layout{ - i, w, h + i, w, h, reference_width, reference_height } } @@ -46,6 +49,14 @@ impl Ir4Layout{ } } + fn get_precrop_reference(&self) -> (i32,i32){ + if((self.i.srotate.unwrap_or(0) / 90 + 4) % 2) == 0 { + (self.reference_width, self.reference_height) + } else { + (self.reference_height, self.reference_width) + } + } + fn get_wh_from_all(&self, source: AspectRatio) -> sizing::Result<(Option, Option)>{ let mut w = self.i.w.unwrap_or(-1); let mut h = self.i.h.unwrap_or(-1); @@ -276,7 +287,7 @@ impl Ir4Layout{ let instructions = Ir4Layout::get_instructions(&constraint).expect("aspect_crop is enabled but not supported"); - let ir_layout = Ir4Layout::new(instructions, source_w, source_h); + let ir_layout = Ir4Layout::new(instructions, source_w, source_h, source_w, source_h); let initial_size = AspectRatio::create(source_w, source_h)?; @@ -338,7 +349,7 @@ impl Ir4Layout{ // later consider adding f.sharpen, f.ignorealpha // (up/down).(filter,window,blur,preserve,colorspace,speed) - let initial_crop = self.get_initial_copy_window(precrop_w, precrop_h); + let initial_crop = self.get_initial_copy_window(); let initial_size = sizing::AspectRatio::create(initial_crop[2] - initial_crop[0], initial_crop[3] - initial_crop[1])?; @@ -543,8 +554,20 @@ impl Ir4Layout{ Ok((Self::gravity1d(x,inner.width(), outer.width())?, Self::gravity1d(y, inner.height(), outer.height())?)) } - fn get_initial_copy_window(&self, w: i32, h: i32) -> [i32;4]{ - let floats = self.get_initial_copy_window_floats(w,h); + fn get_initial_copy_window(&self) -> [i32;4]{ + + let (w, h) = self.get_precrop(); + let (ref_w, ref_h) = self.get_precrop_reference(); + + let mut floats = self.get_initial_copy_window_floats(ref_w,ref_h); + + //Re-scale crop values against real width/height (after decoder downscaling) + if ref_w != w || ref_h != h{ + floats[0] = floats[0] * w as f64 / ref_w as f64; + floats[2] = floats[2] * w as f64 / ref_w as f64; + floats[1] = floats[1] * h as f64 / ref_h as f64; + floats[3] = floats[3] * h as f64 / ref_h as f64; + } let maximums = [w, h]; let ints = floats.iter().enumerate().map(|(ix, item)| { cmp::max(0i32, cmp::min(item.round() as i32, maximums[ix % 2])) @@ -572,9 +595,11 @@ impl Ir4Layout{ if ix < 2 && v < 0f64 || ix > 1 && v <= 0f64{ v += max_dimension; //Support negative offsets from bottom right. } + if v < 0f64 { v = 0f64;} + if v > max_dimension { v = max_dimension; } v }).collect::>(); - if floats[3] <= floats[1] || floats[2] <= floats[0] { + if floats[3].round() <= floats[1].round() || floats[2].round() <= floats[0].round() { //violation of X2 > X1 or Y2 > Y1 defaults }else{ @@ -630,7 +655,7 @@ impl FramewiseBuilder { fn test_crop_and_scale(){ let mut b = FramewiseBuilder::new(); - let l = Ir4Layout::new(Instructions{w: Some(100), h: Some(200), mode: Some(FitMode::Crop), .. Default::default() }, 768, 433); + let l = Ir4Layout::new(Instructions{w: Some(100), h: Some(200), mode: Some(FitMode::Crop), .. Default::default() }, 768, 433, 768, 433); l.add_steps(&mut b, &None).unwrap(); assert_eq!(b.steps, vec![s::Node::Crop { x1: 275, y1: 0, x2: 492, y2: 433 }, @@ -649,6 +674,36 @@ fn test_crop_and_scale(){ }]); } +#[test] +fn test_custom_crop_with_preshrink(){ + let mut b = FramewiseBuilder::new(); + + let l = Ir4Layout::new(Instructions{ + w: Some(170), + h: Some(220), + mode: Some(FitMode::Crop), + scale: Some(ScaleMode::Both), + crop: Some([449f64,0f64,-472f64,0f64]), + .. Default::default() }, + 641, 960, 2560, 1707); //TODO: plug in preshrink values + l.add_steps(&mut b, &None).unwrap(); + + assert_eq!(b.steps, vec![s::Node::Crop { x1: 112, y1: 214, x2: 523, y2: 746 }, + s::Node::Resample2D { + w: 170, + h: 220, + hints: Some(s::ResampleHints { + sharpen_percent: None, + down_filter: None, + up_filter: None, + scaling_colorspace: None, + background_color: Some(s::Color::Transparent), + resample_when: Some(s::ResampleWhen::SizeDiffersOrSharpeningRequested), + sharpen_when: None + }) + }]); +} + #[test] fn test_scale(){ @@ -664,7 +719,7 @@ fn test_scale(){ min_canvas_width: None, min_canvas_height: None }; - let l = Ir4Layout::new(Instructions{w: Some(2560), h: Some(1696), mode: Some(FitMode::Max), f_sharpen_when: Some(SharpenWhen::Downscaling), .. Default::default() }, 5104, 3380); + let l = Ir4Layout::new(Instructions{w: Some(2560), h: Some(1696), mode: Some(FitMode::Max), f_sharpen_when: Some(SharpenWhen::Downscaling), .. Default::default() }, 5104, 3380,5104, 3380); l.add_steps(&mut b, &Some(vec![w.clone()])).unwrap(); assert_eq!(b.steps, vec![s::Node::Resample2D { w: 2560, h: 1696, diff --git a/imageflow_riapi/src/ir4/mod.rs b/imageflow_riapi/src/ir4/mod.rs index 09ce66ecf..55477d73f 100644 --- a/imageflow_riapi/src/ir4/mod.rs +++ b/imageflow_riapi/src/ir4/mod.rs @@ -63,7 +63,7 @@ impl Ir4Translate{ - pub fn get_decode_node(&self) -> Option{ + pub fn get_decode_node_without_commands(&self) -> Option{ if let Some(id) = self.decode_id { Some(s::Node::Decode { io_id: id, commands: None }) }else{ @@ -76,7 +76,7 @@ impl Ir4Translate{ let mut b = crate::ir4::layout::FramewiseBuilder::new(); //Expand decoder early if trimming let delayed_id = if r.parsed.trim_whitespace_threshold.is_some() { - if let Some(n) = self.get_decode_node() { + if let Some(n) = self.get_decode_node_without_commands() { b.add(n); } None @@ -140,24 +140,32 @@ impl Ir4SourceFrameInfo{ pub struct Ir4Expand{ pub i: Ir4Command, pub source: Ir4SourceFrameInfo, + pub reference_width: i32, + pub reference_height: i32, pub encode_id: Option, pub watermarks: Option> } impl Ir4Expand{ - pub fn get_decode_commands(&self) -> sizing::Result>> { //TODO: consider smallvec or generalizing decoder hints + pub fn get_preshrink_ratio(&self) -> sizing::Result{ let i = self.i.parse()?.parsed; - // Default to gamma correct - let gamma_correct = i.down_colorspace != Some(ScalingColorspace::Srgb); - let layout = self.get_layout(&i)?; let (from, to): (AspectRatio, AspectRatio) = layout.get_downscaling()?; let downscale_ratio = (f64::from(from.w) / f64::from(to.w)).min(f64::from(from.h) / f64::from(to.w)); - let preshrink_ratio = i.min_precise_scaling_ratio.unwrap_or(2.1f64) / downscale_ratio; + Ok(i.min_precise_scaling_ratio.unwrap_or(2.1f64) / downscale_ratio) + } + + pub fn get_decode_commands(&self) -> sizing::Result>> { //TODO: consider smallvec or generalizing decoder hints + let i = self.i.parse()?.parsed; + + // Default to gamma correct + let gamma_correct = i.down_colorspace != Some(ScalingColorspace::Srgb); + + let preshrink_ratio = self.get_preshrink_ratio()?; let scaled_width = (f64::from(self.source.w) * preshrink_ratio).floor() as i64; let scaled_height = (f64::from(self.source.h) * preshrink_ratio).floor() as i64; @@ -201,7 +209,7 @@ impl Ir4Expand{ if i.trim_whitespace_threshold.is_some() { return Err(sizing::LayoutError::ContentDependent); } - Ok(layout::Ir4Layout::new(*i, self.source.w, self.source.h)) + Ok(layout::Ir4Layout::new(*i, self.source.w, self.source.h, self.reference_width, self.reference_height)) } pub fn expand_steps(&self) -> sizing::Result {