Add function for resizing to frame without cropping.
If I resize an image to fit a frame using resize_to_fill, the image is cropped if the image is not a perfect square. Can we implement a function for resizing to fill a frame without cropping?
I was previously doing this step manually by overlaying the image on to a black ImageBuffer, but it's a pain to do, since I had to calculate the overlay position for it to be centered. It would be a very useful addition to the crate.
You may want either resize, which preserves aspect ratio, or resize_exact, which does not. Neither of those will crop the image.
@shssoichiro The resize function will make the image fit the bounds, but not fill it. Let's say I have a 64x32 image which I resize to 128x128. The output image will be 128x64. I need an output of 128x128, with the image in the center of the frame.
resize_exact resizes the image to fill a frame but crops out parts outside the frame. Using this, I will get my 128x128 image, but there will be 32 pixels on the top and bottom of the image cut out.
You can resize, then write the result in a SubImage of the correct size. That makes it also possible to choose an arbitrary background. A generally useful function might be a utility to create a centered SubImage, which might be a common operation. But since the other part can be done through a straightforward composition of two functions offered by the library, I feel like it bloats the interface more than it brings to most users.
@HeroicKatora Yeah, I see that it would bloat the crate a bit. Could we compromise and have an operation for overlaying images in a centered matter?
I can understand not wanting to bloat the API. That said, an example of how to achieve this operation using the existing API would be helpful.
If you're willing to add a operation, I'd suggest a "fill" operation, where a user can select how to anchor the image, and then fill to the specified dimensions using a specified background.
@lukehsiao blitting operations tend not to be that hard to implement with simple loops, but there are a bunch of variations that people can want. In the example described above, I'd implement it something like:
fn copy_onto(image: &RgbaImage, background: RgbaImage) -> RgbaImage {
let resized = imageops::resize(&image, 128, 64, filter);
for y in 0..64 {
for x in 0..128 {
background.put_pixel(x, y+32, resized.get_pixel(x,y));
}
}
background
}
Probably not too good of an idea to loop through individual pixels like that. Here's the implemention I wrote. You'll need to tweak it a bit since I ripped it straight out from my project:
let width = original.width() as f32;
let height = original.height() as f32;
// Find the optimum output size.
let size = (width.max(height)).min(MAX_OUTPUT_SIZE);
let scalar = (size / width).min(size / height);
let (xscaled, yscaled) = (scalar * width, scalar * height);
let (xoverlay, yoverlay) = ((size - xscaled) / 2.0, (size - yscaled) / 2.0);
let resized = imageops::resize(
&original,
xscaled as u32,
yscaled as u32,
FilterType::Lanczos3,
);
let mut rescaled = ImageBuffer::new(size as u32, size as u32) as RgbImage;
imageops::overlay(&mut rescaled, &resized, xoverlay as u32, yoverlay as u32);
What's wrong with looping through individual pixels? That's literally what imageops::overlay does.
I would've expected this function just to be a memcpy. I think at the very least rayon should be used to parallelise those loops.