Skip to content

Commit

Permalink
Add Watermark.min_canvas_width and .min_canvas_height
Browse files Browse the repository at this point in the history
  • Loading branch information
lilith committed Jun 11, 2020
1 parent 0859c3b commit 45c1741
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 77 deletions.
8 changes: 6 additions & 2 deletions docs/src/json/watermark.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
Meanings are the same as for [constraint modes](constrain.md#constraint-modes). *Default: `within`*
* `fit_box` can be either `image_percentage` (a box represented by percentages of target image width/height) or
`image_margins` (a box represented by pixels from the edge of the image). *Default `image_margins` 0*
* `min_canvas_width` sets a minimum canvas width below which the watermark will be hidden.
* `min_canvas_height` sets a minimum canvas height below which the watermark will be hidden.
* `opacity` (0..1) How opaque to draw the image. *Default 1.0*
* `hints` See [resampling hints](resampling_hints.md)
#### Example with fit_box: image_percentage
This will align the watermark to 10% from the bottom and right edges of the image,
scaling the watermark down if it takes more than 80% of the image space,
drawing it at 80% opacity and applying 15% sharpening.
drawing it at 80% opacity and applying 15% sharpening. It will not display on images smaller than 50x50px in either dimension.
```json
{
"watermark": {
Expand All @@ -33,7 +35,9 @@ drawing it at 80% opacity and applying 15% sharpening.
"x2": 90,
"y2": 90
}
},
},
"min_canvas_width": 50,
"min_canvas_height": 50,
"opacity": 0.8,
"hints": {
"sharpen_percent": 15
Expand Down
2 changes: 1 addition & 1 deletion imageflow.iml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<sourceFolder url="file://$MODULE_DIR$/c_components/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/c_components/examples" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/c_components/tests" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/c_components/benches" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/imageflow_core/benches" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/imageflow_server/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/imageflow_server/examples" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/imageflow_server/tests" isTestSource="true" />
Expand Down
137 changes: 70 additions & 67 deletions imageflow_core/src/flow/nodes/watermark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,79 +64,82 @@ impl NodeDef for WatermarkDef{
Some(self)
}
}
impl NodeDefOneInputExpand for WatermarkDef{
fn fqn(&self) -> &'static str{
impl NodeDefOneInputExpand for WatermarkDef {
fn fqn(&self) -> &'static str {
"imazen.watermark"
}
fn expand(&self, ctx: &mut OpCtxMut, ix: NodeIndex, params: NodeParams, input: FrameInfo) -> Result<()> {
if let NodeParams::Json(s::Node::Watermark(watermark)) = params {
if let Some((box_x1, box_y1, box_x2, box_y2)) =
WatermarkDef::get_bounding_box(input.w as u32, input.h as u32, watermark.fit_box) {
let box_w = (box_x2 - box_x1) as u32;
let box_h = (box_y2 - box_y1) as u32;

let constraint = imageflow_types::Constraint {
mode: ConstraintMode::from(watermark.fit_mode.unwrap_or(WatermarkConstraintMode::Within)),
w: Some(box_w),
h: Some(box_h),
hints: None,
gravity: watermark.gravity.clone(),
canvas_color: None
};

let watermark_info = ctx.job.get_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

let w = constraint_results.scale_to.width() as u32;
let h = constraint_results.scale_to.height() as u32;
let (x1, y1) = WatermarkDef::obey_gravity(box_x1, box_y1, box_x2, box_y2,
w as i32, h as i32, watermark.gravity)?;


let mut b = Vec::new();

b.push(Node::from(imageflow_types::Node::Decode { io_id: watermark.io_id, commands: None }));

if let Some(c) = constraint_results.crop {
b.push(Node::from(s::Node::Crop { x1: c[0], y1: c[1], x2: c[2], y2: c[3] }));
let canvas_sufficient = (watermark.min_canvas_width.unwrap_or(0) as i32) < input.w &&
(watermark.min_canvas_height.unwrap_or(0) as i32) < input.h;
if canvas_sufficient {
if let Some((box_x1, box_y1, box_x2, box_y2)) =
WatermarkDef::get_bounding_box(input.w as u32, input.h as u32, watermark.fit_box) {
let box_w = (box_x2 - box_x1) as u32;
let box_h = (box_y2 - box_y1) as u32;

let constraint = imageflow_types::Constraint {
mode: ConstraintMode::from(watermark.fit_mode.unwrap_or(WatermarkConstraintMode::Within)),
w: Some(box_w),
h: Some(box_h),
hints: None,
gravity: watermark.gravity.clone(),
canvas_color: None
};

let watermark_info = ctx.job.get_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

let w = constraint_results.scale_to.width() as u32;
let h = constraint_results.scale_to.height() as u32;
let (x1, y1) = WatermarkDef::obey_gravity(box_x1, box_y1, box_x2, box_y2,
w as i32, h as i32, watermark.gravity)?;


let mut b = Vec::new();

b.push(Node::from(imageflow_types::Node::Decode { io_id: watermark.io_id, commands: None }));

if let Some(c) = constraint_results.crop {
b.push(Node::from(s::Node::Crop { x1: c[0], y1: c[1], x2: c[2], y2: c[3] }));
}

let opacity = f32::max(0f32, f32::min(1f32, watermark.opacity.unwrap_or(1f32)));
if opacity < 1f32 {
//TODO: push EnableTransparency node
b.push(Node::from(imageflow_types::Node::ColorFilterSrgb(imageflow_types::ColorFilterSrgb::Alpha(opacity))));
}

b.push(Node::from(
imageflow_types::Node::DrawImageExact {
x: x1 as u32,
y: y1 as u32,
w,
h,
blend: Some(imageflow_types::CompositingMode::Compose),
hints: constraint.hints,
})
);

// Add the watermark chain
let (_, draw_image) = ctx.add_nodes(b).unwrap();
// Locate and add the canvas edge
let canvas = ctx.first_parent_input(ix).expect("watermark must have input node");
ctx.graph.add_edge(canvas, draw_image, EdgeKind::Canvas).unwrap();
// Add outbound nodes
ctx.copy_edges_to(ix, draw_image, EdgeDirection::Outgoing);

// Remove old node and old edges
ctx.graph.remove_node(ix).unwrap();


return Ok(());
}

let opacity = f32::max(0f32, f32::min(1f32, watermark.opacity.unwrap_or(1f32)));
if opacity < 1f32 {
//TODO: push EnableTransparency node
b.push(Node::from(imageflow_types::Node::ColorFilterSrgb(imageflow_types::ColorFilterSrgb::Alpha(opacity))));
}

b.push(Node::from(
imageflow_types::Node::DrawImageExact {
x: x1 as u32,
y: y1 as u32,
w,
h,
blend: Some(imageflow_types::CompositingMode::Compose),
hints: constraint.hints,
})
);

// Add the watermark chain
let (_, draw_image) = ctx.add_nodes(b).unwrap();
// Locate and add the canvas edge
let canvas = ctx.first_parent_input(ix).expect("watermark must have input node");
ctx.graph.add_edge(canvas, draw_image, EdgeKind::Canvas).unwrap();
// Add outbound nodes
ctx.copy_edges_to(ix, draw_image, EdgeDirection::Outgoing);

// Remove old node and old edges
ctx.graph.remove_node(ix).unwrap();


Ok(())
}else{
// The bounding box was too small to draw the watermark
ctx.delete_node_and_snap_together(ix);
Ok(())
}
// The bounding box was too small to draw the watermark
ctx.delete_node_and_snap_together(ix);
Ok(())
} else {
Err(nerror!(crate::ErrorKind::NodeParamsMismatch, "Need Constrain, got {:?}", params))
}
Expand Down
16 changes: 12 additions & 4 deletions imageflow_core/tests/visuals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ fn test_watermark_image() {
gravity: Some(imageflow_types::ConstraintGravity::Percentage {x: 100f32, y: 100f32}),
fit_box: Some(imageflow_types::WatermarkConstraintBox::ImagePercentage {x1: 30f32, y1: 50f32, x2: 90f32, y2: 90f32}),
fit_mode: Some(imageflow_types::WatermarkConstraintMode::FitCrop),
min_canvas_width: None,
min_canvas_height: None,
opacity: Some(0.9f32),
hints: Some(imageflow_types::ResampleHints{
sharpen_percent: Some(15f32),
Expand All @@ -194,7 +196,7 @@ fn test_watermark_image() {
background_color: None,
resample_when: None,
sharpen_when: None
})
}),
})
]
);
Expand All @@ -215,9 +217,11 @@ fn test_watermark_image_command_string() {
encode: None,
watermarks: Some(vec![imageflow_types::Watermark{
io_id: 1,
gravity: Some(imageflow_types::ConstraintGravity::Percentage {x: 100f32, y: 100f32}),
fit_box: Some(imageflow_types::WatermarkConstraintBox::ImagePercentage {x1: 30f32, y1: 50f32, x2: 90f32, y2: 90f32}),
fit_mode: Some(imageflow_types::WatermarkConstraintMode::FitCrop),
gravity: Some(imageflow_types::ConstraintGravity::Percentage {x: 100f32, y: 100f32}),
min_canvas_width: None,
min_canvas_height: None,
opacity: Some(0.9f32),
hints: Some(imageflow_types::ResampleHints{
sharpen_percent: Some(15f32),
Expand All @@ -227,7 +231,8 @@ fn test_watermark_image_command_string() {
background_color: None,
resample_when: None,
sharpen_when: None
})
}),

}
])
}
Expand Down Expand Up @@ -257,6 +262,8 @@ fn test_watermark_image_small() {
gravity: Some(imageflow_types::ConstraintGravity::Percentage {x: 100f32, y: 100f32}),
fit_box: Some(imageflow_types::WatermarkConstraintBox::ImagePercentage {x1: 0f32, y1: 0f32, x2: 90f32, y2: 90f32}),
fit_mode: Some(imageflow_types::WatermarkConstraintMode::Within),
min_canvas_width: None,
min_canvas_height: None,
opacity: Some(0.9f32),
hints: Some(imageflow_types::ResampleHints{
sharpen_percent: Some(15f32),
Expand All @@ -266,7 +273,8 @@ fn test_watermark_image_small() {
background_color: None,
resample_when: None,
sharpen_when: None
})
}),

})
]
);
Expand Down
4 changes: 3 additions & 1 deletion imageflow_riapi/src/ir4/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,9 @@ fn test_scale(){
fit_box: None,
fit_mode: None,
opacity: None,
hints: None
hints: None,
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);
l.add_steps(&mut b, &Some(vec![w.clone()])).unwrap();
Expand Down
6 changes: 4 additions & 2 deletions imageflow_types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -537,11 +537,13 @@ pub enum WatermarkConstraintBox{
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
pub struct Watermark{
pub io_id: i32,
pub gravity: Option<ConstraintGravity>,
pub fit_box: Option<WatermarkConstraintBox>,
pub fit_mode: Option<WatermarkConstraintMode>,
pub gravity: Option<ConstraintGravity>,
pub min_canvas_width: Option<u32>,
pub min_canvas_height: Option<u32>,
pub opacity: Option<f32>,
pub hints: Option<ResampleHints>
pub hints: Option<ResampleHints>,
}

/// Blend pixels (if transparent) or replace?
Expand Down

0 comments on commit 45c1741

Please sign in to comment.