Closed hillin closed 8 months ago
Hi, you can assume that this copy_from
is a simple crop operation.
With that in mind and if you are willing to add a dependency , zune-imageprocs
, you can use the crop filter
fn main() {
//image dimensions
let im_width = 1000;
let im_height = 1000;
let mut image = Image::fill(255_u8, ColorSpace::RGBA, im_width, im_height);
let x = 10;
let y = 10;
let new_width = im_width - x;
let new_height = im_height - y;
// creates a new image, and applies the operation on it,
// better if you don't want to modify the original image
let new_image = Crop::new(new_width, new_height, x, y).clone_and_execute(&image).unwrap();
// modifies in place
// better if memory is a concern
Crop::new(new_width, new_height, x, y).execute(&mut image).unwrap();
}
Above, x and y correspond to the same parameters as copy_from
in image-rs.
Crop docs https://docs.rs/zune-imageprocs/latest/zune_imageprocs/crop/index.html
Hope that helps.
Feel free to ask any question if there is another concern
Thanks @etemesi254 !
The copy_from
does a little bit more than a simple cropping: it copies image A to image B at a given position on image B, overwrites the pixels that image A covers, while keeping other pixels on image B untouched. In another word, we "draw" image A on image B.
Also notice that image-rs has the concept of "view", which is a virtual image that is projected to a rectangular region of a "real" image (bitmap). Unlike cropping, no new image is allocated when creating a view. In the example above, image A can be a view of another image A', hence we can copy a region of image A' to the specified position on image B, by combining these two operations.
Then that's a really bad function name in my opinion then.
What you are trying to achieve is to composite one image over another, which most libraries call it composite. This takes into account alpha channels. Now this is way more complicated and to get it correctly is difficult, see https://www.youtube.com/watch?v=XobSAXZaKJ8
But we still support it, via the Composite
filter, although not yet fully complete, it can perform the same emulation as before.
It wasn't documented so sorry for that.
Here's an example of me putting a logo on bottom right
use zune_core::colorspace::ColorSpace;
use zune_image::image::Image;
use zune_image::traits::OperationsTrait;
use zune_imageprocs::composite::{Composite, CompositeMethod};
use zune_imageprocs::utils::Gravity;
fn composite_over() {
// logo image
let mut src_image = Image::open("logo.png").unwrap();
// background image
let mut dst_image = Image::open("wallpaper.jpg").unwrap();
// Convert to RGBA since the logo has a transparent background,
// the composite operation needs images with the same colorspace
// if the colorspaces differ this is an error
src_image.convert_color(ColorSpace::RGBA).unwrap();
dst_image.convert_color(ColorSpace::RGBA).unwrap();
// It is recommended to pre-multiply before blending, so do that
PremultiplyAlpha::new(AlphaState::PreMultiplied)
.execute(&mut src_image)
.unwrap();
// it's called try_new because one value, either Gravity or Dimensions have to be specified
// not both at the same time
let composite = Composite::try_new(
&src_image,
CompositeMethod::Over,
None,
Some(Gravity::BottomRight)
).unwrap();
// run the operation.
composite.execute(&mut dst_image).unwrap();
// save image, the save operation will take care of converting it to an RGB, jpeg doesn't support RGBA
dst_image.save("./composite.jpg").unwrap();
}
Also notice that image-rs has the concept of "view", which is a virtual image that is projected to a rectangular region of a "real" image (bitmap). Unlike cropping, no new image is allocated when creating a view. In the example above, image A can be a view of another image A', hence we can copy a region of image A' to the specified position on image B, by combining these two operations.
I think this is a sub-image, currently unimplemented as it complicates other things, but it can be added if there is a need
Great! Composite
should definitely do the job.
We can say Composite
generalizes what image-rs' copy_from
does (by supporting alpha blending, or even bit blitting - I don't know), but to my understanding there is still some difference here: the copy
in the bad naming actually implies that it's a fast block copy operation (which may prove to be difficult to implement in zune-image as it has a different memory model as far as I'm concerned).
Anyways, I'll try the Composite
approach! Thanks for the help!
We can say Composite generalizes what image-rs' copy_from does (by supporting alpha blending, or even bit blitting - I don't know), but to my understanding there is still some difference here: the copy in the bad naming actually implies that it's a fast block copy operation (which may prove to be difficult to implement in zune-image as it has a different memory model as far as I'm concerned).
The operation can be done quickly, that isn't hard to do
Doing it correctly is more difficult
I chose to do it correctly over quickly
With the help of Composite
(plus the half-completed Resize), we have successfully migrated from image-rs to zune-image.
Along the way we created a benchmark here: https://github.com/hillin/image-rs-vs-zune-image. It measures the performance of one simple task, which is an extremely hot path in our project: render a rectangular region(a
) from image A, onto another rectangular region(b
) on image B. This involves cropping the region a
from A, resize it to fit the region b
and composite it to B.
With image-rs we use GenericImageView::view
to crop (actually, create a sub-image), imageops::resize
for resizing, and GenericImage::copy_from
to composite. In zune-image these are replaced with zune_imageprocs::crop::Crop
, zune_imageprocs::resize::Resize
and zune_imageprocs::composite::Composite
respectively.
Benchmark result on i9-9900K (3.60 GHz) + 32GB RAM, using criterion:
// load is loading a 512x512 lenna.jpg
image-rs load time: [2.0388 ms 2.0668 ms 2.0973 ms]
zune-image load time: [1.1924 ms 1.2063 ms 1.2206 ms]
image-rs render time: [3.0728 ms 3.1115 ms 3.1531 ms]
zune-image render time: [1.7630 ms 1.7722 ms 1.7814 ms]
The benchmark showed that zune-image is remarkably fast, 45% faster than image-rs on average. Great job on this amazing lib!
Thanks, hope the multiple APIs didn't bog you down.
The architecture is that way for easy optimizations of each operation.
The API design is not a problem at all and I'm quite happy with them. Keep the great job up!
In image-rs we can do this with the
copy_from
method.Sorry but I didn't find any hint in the docs or code. If zune-image does not have this yet, this issue acts as a feature request too.