image icon indicating copy to clipboard operation
image copied to clipboard

Why is the FromColor trait not public?

Open Drevoed opened this issue 5 years ago • 4 comments

How does one create a type constraint on a generic function that allows an image to be converted into the pixel type that the caller wants to get?

Overall, I'm trying to achieve creating a fully white RGBA image and then return it with the the type that is needed elsewhere. So far I tried this:

use imageproc::drawing::draw_filled_rect_mut;
use image::buffer::ConvertBuffer;
use image::{GenericImage, ImageBuffer, Pixel, RgbaImage, Rgba, GenericImageView};
use imageproc::rect::Rect;

pub(crate) fn create_square_image<I>(source_img: &I, size: u32) -> ImageBuffer<I::Pixel, Vec<u8>>
where
    I: ConvertBuffer<ImageBuffer<<I as GenericImageView>::Pixel, Vec<u8>>> + GenericImage,
    <I as GenericImageView>::Pixel: Pixel<Subpixel = u8>
{
    let (width, height) = source_img.dimensions();

    let mut block_image = create_white_image(size, size);

    let delta_x = (size - width) / 2;
    let delta_y = (size - height) / 2;

    for y in 0..height {
        let target_y = y + delta_y;
        if target_y >= size {
            continue;
        }
        for x in 0..width {
            let target_x = x + delta_x;
            if target_x >= size {
                continue;
            }
            let pixel = source_img.get_pixel(x, y);
            block_image.put_pixel(x, y, pixel)
        }
    }

    block_image
}

pub(crate) fn create_white_image<I>(width: u32, height: u32) -> ImageBuffer<I::Pixel, Vec<u8>>
where
    I: ConvertBuffer<ImageBuffer<<I as GenericImageView>::Pixel, Vec<u8>>> + GenericImage,
    <I as GenericImageView>::Pixel: Pixel<Subpixel = u8>
{
    let mut image = ImageBuffer::new(width, height);
    draw_filled_rect_mut(&mut image, Rect::at(0, 0).of_size(width, height), Rgba([255u8, 255, 255, 255]));

    image.convert()
}

but it doesn't work because P doesn't have a type constraint FromColor<Rgba<u8>>:

error[E0277]: the trait bound `<I as image::image::GenericImageView>::Pixel: image::color::FromColor<image::color::Rgba<u8>>` is not satisfied
  --> src\main.rs:44:11
   |
39 |     <I as GenericImageView>::Pixel: Pixel<Subpixel = u8>
   |                                                         - help: consider further restricting the associated type: `, <I as image::image::GenericImageView>::Pixel: image::color::FromColor<image::color::Rgba<u8>>`
...
44 |     image.convert()
   |           ^^^^^^^ the trait `image::color::FromColor<image::color::Rgba<u8>>` is not implemented for `<I as image::image::GenericImageView>::Pixel`
   |
   = note: required because of the requirements on the impl of `image::buffer_::ConvertBuffer<image::buffer_::ImageBuffer<<I as image::image::GenericImageView>::Pixel, std::vec::Vec<u8>>>` for `image::buffer_::ImageBuffer<image::color::Rgba<u8>, std::vec::Vec<u8>>`

cargo.toml:

[dependencies]
image = "0.23.4"
imageproc = "0.20.0"

rustup toolchain:

active toolchain
----------------

stable-x86_64-pc-windows-gnu (default)
rustc 1.43.0 (4fb7144ed 2020-04-20)

How do I get around this?

Thanks in advance.

Drevoed avatar May 05 '20 12:05 Drevoed

I managed to make it work with the exported FromColor trait in local crate like this:

use imageproc::drawing::draw_filled_rect_mut;
use image::buffer::ConvertBuffer;
use image::{GenericImage, ImageBuffer, Pixel, RgbaImage, Rgba, GenericImageView, FromColor, open, GrayAlphaImage, Bgra, Rgb};
use imageproc::rect::Rect;

pub(crate) fn create_square_image<I>(source_img: &I, size: i32) -> ImageBuffer<I::Pixel, Vec<u8>>
where
    I: ConvertBuffer<ImageBuffer<<I as GenericImageView>::Pixel, Vec<u8>>> + GenericImage,
    <I as GenericImageView>::Pixel: Pixel<Subpixel = u8> + FromColor<Rgba<u8>> + 'static
{
    let (width, height) = source_img.dimensions();

    let mut block_image = create_white_image(size as u32, size as u32);

    let delta_x = (size - width as i32) / 2;
    let delta_y = (size - height as i32) / 2;

    for y in 0..height {
        let target_y = y as i32 + delta_y;
        if target_y < 0 || target_y >= size {
            continue;
        }
        for x in 0..width {
            let target_x = x as i32 + delta_x;
            if target_x < 0 || target_x >= size {
                continue;
            }
            let pixel = source_img.get_pixel(x, y);
            block_image.put_pixel(target_x as u32, target_y as u32, pixel)
        }
    }

    block_image
}

pub(crate) fn create_white_image<P>(width: u32, height: u32) -> ImageBuffer<P, Vec<u8>>
where
    P: Pixel<Subpixel = u8> + FromColor<Rgba<u8>> + 'static
{
    let mut image = ImageBuffer::new(width, height);
    draw_filled_rect_mut(&mut image, Rect::at(0, 0).of_size(width, height), Rgba([255u8, 255, 255, 255]));

    image.convert()
}

so is there any reason to not export FromColor?

Thanks again in advance.

Drevoed avatar May 05 '20 13:05 Drevoed

I also wished that FromColor or FromPrimitive traits were public. OTOH, I recognize that making FromColor and/or FromPrimitive traits public is just one possible solution to the actual problem I am trying to solve. The actual problem is to have an equivalent of DynamicImage::into_rgba8, but where the results don't go into a newly allocated Vec<u8> but instead the target buffer (e.g. &mut [u8]) is provided by the caller (in my case, the buffer will be allocated in C++, either via std::vector or SkBitmap::tryAllocN32Pixels).

For now I have temporarily copy&pasted code into my crate (see here), but it would be great if the public API of image supported by use-case.

anforowicz avatar Sep 12 '23 18:09 anforowicz

Yeah, this crate doesn't currently have a great story for color conversions unless you are using DynamicImage. Even there we don't do any handling of color spaces or gamma correction or anything beyond simple type level coversions between u8 <--> u16 <--> f32.

One issue with simply making FromColor and FromPrimitive traits public is that it wouldn't be that great of an API. Notice that the linked chromium code requires a bunch of additional boiler plate including the convert_pixel_format! macro and a match statement calling that macro for each color type. So this is an area were we'd like to have a good API to achieve something like this, but because the functionality is possible outside the crate designing an API has been relatively low priority.

(As a side note, PNGs can't store f32 images so the code paths dealing with them are unreachable.)

fintelia avatar Sep 14 '23 06:09 fintelia

Worth noting is that libpng supports 2 different methods for u16 -> u8 conversions: png_do_chop (just discard 8 least significant bits and use the 8 most significant bits) and png_do_scale_16_to_8 (more accurate scaling). Both methods have their applications - each is useful in slightly different scenarios. The point I am trying to make is that I recognize that supporting both modes via a single FromPrimitive (or FromColor) may be challenging. Maybe this is indeed something that should be done outside of the image crate.

anforowicz avatar Sep 14 '23 15:09 anforowicz