image icon indicating copy to clipboard operation
image copied to clipboard

Add function for resizing to frame without cropping.

Open cedws opened this issue 7 years ago • 9 comments

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.

cedws avatar May 06 '18 21:05 cedws

You may want either resize, which preserves aspect ratio, or resize_exact, which does not. Neither of those will crop the image.

shssoichiro avatar May 06 '18 22:05 shssoichiro

@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.

cedws avatar May 06 '18 22:05 cedws

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.

197g avatar May 06 '18 23:05 197g

@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?

cedws avatar May 07 '18 00:05 cedws

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 avatar Apr 02 '19 17:04 lukehsiao

@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
}

fintelia avatar Apr 02 '19 18:04 fintelia

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);

cedws avatar Apr 02 '19 19:04 cedws

What's wrong with looping through individual pixels? That's literally what imageops::overlay does.

fintelia avatar Apr 02 '19 23:04 fintelia

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.

cedws avatar Apr 03 '19 14:04 cedws