Skip to content

Commit

Permalink
Merge pull request #2195 from barsae/master
Browse files Browse the repository at this point in the history
Preserve the aspect ratio of a clipped region in an Image
  • Loading branch information
richard-uk1 authored Jul 12, 2022
2 parents 380f1c5 + 579f5ac commit bf5d3ac
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 15 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ You can find its changes [documented below](#070---2021-01-01).
- `ListIter` implementations for `Vector<T>` and `(S, Vector<T>)` ([#1967] by [@xarvic])
- Do not panic in Application::try_global if Application is not created ([#1996] by [@Maan2003])
- X11: window focus events ([#1938] by [@Maan2003]
- Preserve the aspect ratio of a clipped region in an Image ([#2195] by [@barsae])

### Visual

Expand Down Expand Up @@ -558,6 +559,7 @@ Last release without a changelog :(
[@twitchyliquid64]: https://github.com/twitchyliquid64
[@dristic]: https://github.com/dristic
[@NickLarsenNZ]: https://github.com/NickLarsenNZ
[@barsae]: https://github.com/barsae

[#599]: https://github.com/linebender/druid/pull/599
[#611]: https://github.com/linebender/druid/pull/611
Expand Down Expand Up @@ -851,6 +853,7 @@ Last release without a changelog :(
[#2157]: https://github.com/linebender/druid/pull/2157
[#2158]: https://github.com/linebender/druid/pull/2158
[#2172]: https://github.com/linebender/druid/pull/2172
[#2195]: https://github.com/linebender/druid/pull/2195
[#2196]: https://github.com/linebender/druid/pull/2196
[#2203]: https://github.com/linebender/druid/pull/2203

Expand Down
66 changes: 65 additions & 1 deletion druid/examples/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use druid::widget::{prelude::*, FillStrat, Image};
use druid::widget::{
Checkbox, CrossAxisAlignment, Flex, Label, RadioGroup, SizedBox, TextBox, WidgetExt,
};
use druid::{AppLauncher, Color, Data, ImageBuf, Lens, WindowDesc};
use druid::{AppLauncher, Color, Data, ImageBuf, Lens, Rect, WindowDesc};

static FILL_STRAT_OPTIONS: &[(&str, FillStrat)] = &[
("Contain", FillStrat::Contain),
Expand All @@ -50,6 +50,11 @@ struct AppState {
width: f64,
fix_height: bool,
height: f64,
clip: bool,
clip_x: f64,
clip_y: f64,
clip_width: f64,
clip_height: f64,
}

/// builds a child Flex widget from some paramaters.
Expand Down Expand Up @@ -144,6 +149,8 @@ fn make_control_row() -> impl Widget<AppState> {
.with_default_spacer()
.with_child(Checkbox::new("Fix height").lens(AppState::fix_height))
.with_default_spacer()
.with_child(Checkbox::new("Clip").lens(AppState::clip))
.with_default_spacer()
.with_child(Checkbox::new("set interpolation mode").lens(AppState::interpolate)),
)
.padding(10.0)
Expand All @@ -164,6 +171,28 @@ fn make_width() -> impl Widget<AppState> {
.fix_width(60.0),
),
)
.with_default_spacer()
.with_child(Label::new("clip x:"))
.with_default_spacer()
.with_child(
Flex::row().with_child(
TextBox::new()
.with_formatter(ParseFormatter::new())
.lens(AppState::clip_x)
.fix_width(60.0),
),
)
.with_default_spacer()
.with_child(Label::new("clip width:"))
.with_default_spacer()
.with_child(
Flex::row().with_child(
TextBox::new()
.with_formatter(ParseFormatter::new())
.lens(AppState::clip_width)
.fix_width(60.0),
),
)
}
fn make_height() -> impl Widget<AppState> {
Flex::column()
Expand All @@ -178,6 +207,28 @@ fn make_height() -> impl Widget<AppState> {
.fix_width(60.0),
),
)
.with_default_spacer()
.with_child(Label::new("clip y:"))
.with_default_spacer()
.with_child(
Flex::row().with_child(
TextBox::new()
.with_formatter(ParseFormatter::new())
.lens(AppState::clip_y)
.fix_width(60.0),
),
)
.with_default_spacer()
.with_child(Label::new("clip height:"))
.with_default_spacer()
.with_child(
Flex::row().with_child(
TextBox::new()
.with_formatter(ParseFormatter::new())
.lens(AppState::clip_height)
.fix_width(60.0),
),
)
}

fn build_widget(state: &AppState) -> Box<dyn Widget<AppState>> {
Expand All @@ -187,6 +238,14 @@ fn build_widget(state: &AppState) -> Box<dyn Widget<AppState>> {
if state.interpolate {
img.set_interpolation_mode(state.interpolation_mode)
}
if state.clip {
img.set_clip_area(Some(Rect::new(
state.clip_x,
state.clip_y,
state.clip_x + state.clip_width,
state.clip_y + state.clip_height,
)));
}
let mut sized = SizedBox::new(img);
if state.fix_width {
sized = sized.fix_width(state.width)
Expand Down Expand Up @@ -219,6 +278,11 @@ pub fn main() {
width: 200.,
fix_height: true,
height: 100.,
clip: false,
clip_x: 0.,
clip_y: 0.,
clip_width: 50.,
clip_height: 50.,
};

AppLauncher::with_window(main_window)
Expand Down
28 changes: 14 additions & 14 deletions druid/src/widget/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,14 @@ impl Image {
fn invalidate(&mut self) {
self.paint_data = None;
}

/// The size of the effective image, considering clipping if it's in effect.
#[inline]
fn image_size(&mut self) -> Size {
self.clip_area
.map(|a| a.size())
.unwrap_or_else(|| self.image_data.size())
}
}

impl<T: Data> Widget<T> for Image {
Expand Down Expand Up @@ -193,23 +201,24 @@ impl<T: Data> Widget<T> for Image {
// in the size exactly. If it is unconstrained by both width and height take the size of
// the image.
let max = bc.max();
let image_size = self.image_data.size();
let image_size = self.image_size();
let size = if bc.is_width_bounded() && !bc.is_height_bounded() {
let ratio = max.width / image_size.width;
Size::new(max.width, ratio * image_size.height)
} else if bc.is_height_bounded() && !bc.is_width_bounded() {
let ratio = max.height / image_size.height;
Size::new(ratio * image_size.width, max.height)
} else {
bc.constrain(self.image_data.size())
bc.constrain(image_size)
};
trace!("Computed size: {}", size);
size
}

#[instrument(name = "Image", level = "trace", skip(self, ctx, _data, _env))]
fn paint(&mut self, ctx: &mut PaintCtx, _data: &T, _env: &Env) {
let offset_matrix = self.fill.affine_to_fill(ctx.size(), self.image_data.size());
let image_size = self.image_size();
let offset_matrix = self.fill.affine_to_fill(ctx.size(), image_size);

// The ImageData's to_piet function does not clip to the image's size
// CairoRenderContext is very like druids but with some extra goodies like clip
Expand All @@ -236,18 +245,9 @@ impl<T: Data> Widget<T> for Image {
};
ctx.transform(offset_matrix);
if let Some(area) = self.clip_area {
ctx.draw_image_area(
piet_image,
area,
self.image_data.size().to_rect(),
self.interpolation,
);
ctx.draw_image_area(piet_image, area, image_size.to_rect(), self.interpolation);
} else {
ctx.draw_image(
piet_image,
self.image_data.size().to_rect(),
self.interpolation,
);
ctx.draw_image(piet_image, image_size.to_rect(), self.interpolation);
}
});
}
Expand Down

0 comments on commit bf5d3ac

Please sign in to comment.