Add some additional image effect nodes (#869)

* Move the Subpath type to graphene-std

* Add the transform subpath node

* Delete selected nodes

* Inserting node list on right click

* Add several bitmap manipulator nodes

* Convert add node to use f64

* Add posterize node

* Rename names randomly

* Fix naming

* Exposure node

* Fix typo

* Adjust exposure node range

* Comment out vector nodes

* Adjust exposure range again

* Posterise as ints

* Rename input

* Use >= in the to hsl function
This commit is contained in:
0HyperCube
2022-12-03 22:29:45 +00:00
committed by GitHub
parent 0fb12bf39b
commit f9d772070b
50 changed files with 1310 additions and 481 deletions

View File

@@ -176,89 +176,286 @@ pub fn export_image_node<'n>() -> impl Node<(Image, &'n str), Output = Result<()
})
}
fn grayscale_image(mut image: Image) -> Image {
for pixel in &mut image.data {
let avg = (pixel.r() + pixel.g() + pixel.b()) / 3.;
*pixel = Color::from_rgbaf32_unchecked(avg, avg, avg, pixel.a());
}
image
}
#[derive(Debug, Clone, Copy)]
pub struct GrayscaleImageNode;
impl Node<Image> for GrayscaleImageNode {
type Output = Image;
fn eval(self, mut image: Image) -> Image {
for pixel in &mut image.data {
let avg = (pixel.r() + pixel.g() + pixel.b()) / 3.;
*pixel = Color::from_rgbaf32_unchecked(avg, avg, avg, pixel.a());
}
image
fn eval(self, image: Image) -> Image {
grayscale_image(image)
}
}
impl Node<Image> for &GrayscaleImageNode {
type Output = Image;
fn eval(self, mut image: Image) -> Image {
for pixel in &mut image.data {
let avg = (pixel.r() + pixel.g() + pixel.b()) / 3.;
*pixel = Color::from_rgbaf32_unchecked(avg, avg, avg, pixel.a());
}
image
fn eval(self, image: Image) -> Image {
grayscale_image(image)
}
}
fn invert_image(mut image: Image) -> Image {
for pixel in &mut image.data {
*pixel = Color::from_rgbaf32_unchecked(1. - pixel.r(), 1. - pixel.g(), 1. - pixel.b(), pixel.a());
}
image
}
#[derive(Debug, Clone, Copy)]
pub struct BrightenImageNode<N: Node<(), Output = f32>>(N);
pub struct InvertImageColorNode;
impl<N: Node<(), Output = f32>> Node<Image> for BrightenImageNode<N> {
impl Node<Image> for InvertImageColorNode {
type Output = Image;
fn eval(self, mut image: Image) -> Image {
let brightness = self.0.eval(());
let per_channel = |col: f32| (col + brightness / 255.).clamp(0., 1.);
for pixel in &mut image.data {
*pixel = Color::from_rgbaf32_unchecked(per_channel(pixel.r()), per_channel(pixel.g()), per_channel(pixel.b()), pixel.a());
}
image
fn eval(self, image: Image) -> Image {
invert_image(image)
}
}
impl<N: Node<(), Output = f32> + Copy> Node<Image> for &BrightenImageNode<N> {
impl Node<Image> for &InvertImageColorNode {
type Output = Image;
fn eval(self, mut image: Image) -> Image {
let brightness = self.0.eval(());
let per_channel = |col: f32| (col + brightness / 255.).clamp(0., 1.);
for pixel in &mut image.data {
*pixel = Color::from_rgbaf32_unchecked(per_channel(pixel.r()), per_channel(pixel.g()), per_channel(pixel.b()), pixel.a());
}
image
fn eval(self, image: Image) -> Image {
invert_image(image)
}
}
impl<N: Node<(), Output = f32> + Copy> BrightenImageNode<N> {
fn shift_image_hsl(mut image: Image, hue_shift: f32, saturation_shift: f32, luminance_shift: f32) -> Image {
for pixel in &mut image.data {
let [hue, saturation, luminance, alpha] = pixel.to_hsla();
*pixel = Color::from_hsla(
(hue + hue_shift / 360.) % 1.,
(saturation + saturation_shift / 100.).clamp(0., 1.),
(luminance + luminance_shift / 100.).clamp(0., 1.),
alpha,
);
}
image
}
#[derive(Debug, Clone, Copy)]
pub struct ShiftImageHSLNode<Hue, Sat, Lum>
where
Hue: Node<(), Output = f64>,
Sat: Node<(), Output = f64>,
Lum: Node<(), Output = f64>,
{
hue: Hue,
saturation: Sat,
luminance: Lum,
}
impl<Hue, Sat, Lum> Node<Image> for ShiftImageHSLNode<Hue, Sat, Lum>
where
Hue: Node<(), Output = f64>,
Sat: Node<(), Output = f64>,
Lum: Node<(), Output = f64>,
{
type Output = Image;
fn eval(self, image: Image) -> Image {
shift_image_hsl(image, self.hue.eval(()) as f32, self.saturation.eval(()) as f32, self.luminance.eval(()) as f32)
}
}
impl<Hue, Sat, Lum> Node<Image> for &ShiftImageHSLNode<Hue, Sat, Lum>
where
Hue: Node<(), Output = f64> + Copy,
Sat: Node<(), Output = f64> + Copy,
Lum: Node<(), Output = f64> + Copy,
{
type Output = Image;
fn eval(self, image: Image) -> Image {
shift_image_hsl(image, self.hue.eval(()) as f32, self.saturation.eval(()) as f32, self.luminance.eval(()) as f32)
}
}
impl<Hue, Sat, Lum> ShiftImageHSLNode<Hue, Sat, Lum>
where
Hue: Node<(), Output = f64>,
Sat: Node<(), Output = f64>,
Lum: Node<(), Output = f64>,
{
pub fn new(hue: Hue, saturation: Sat, luminance: Lum) -> Self {
Self { hue, saturation, luminance }
}
}
// Copy pasta from https://stackoverflow.com/questions/2976274/adjust-bitmap-image-brightness-contrast-using-c
fn adjust_image_brightness_and_contrast(mut image: Image, brightness_shift: f32, contrast: f32) -> Image {
let factor = (259. * (contrast + 255.)) / (255. * (259. - contrast));
let channel = |channel: f32| ((factor * (channel * 255. + brightness_shift - 128.) + 128.) / 255.).clamp(0., 1.);
for pixel in &mut image.data {
*pixel = Color::from_rgbaf32_unchecked(channel(pixel.r()), channel(pixel.g()), channel(pixel.b()), pixel.a())
}
image
}
#[derive(Debug, Clone, Copy)]
pub struct ImageBrightnessAndContrast<Brightness, Contrast>
where
Brightness: Node<(), Output = f64>,
Contrast: Node<(), Output = f64>,
{
brightness: Brightness,
contrast: Contrast,
}
impl<Brightness, Contrast> Node<Image> for ImageBrightnessAndContrast<Brightness, Contrast>
where
Brightness: Node<(), Output = f64>,
Contrast: Node<(), Output = f64>,
{
type Output = Image;
fn eval(self, image: Image) -> Image {
adjust_image_brightness_and_contrast(image, self.brightness.eval(()) as f32, self.contrast.eval(()) as f32)
}
}
impl<Brightness, Contrast> Node<Image> for &ImageBrightnessAndContrast<Brightness, Contrast>
where
Brightness: Node<(), Output = f64> + Copy,
Contrast: Node<(), Output = f64> + Copy,
{
type Output = Image;
fn eval(self, image: Image) -> Image {
adjust_image_brightness_and_contrast(image, self.brightness.eval(()) as f32, self.contrast.eval(()) as f32)
}
}
impl<Brightness, Contrast> ImageBrightnessAndContrast<Brightness, Contrast>
where
Brightness: Node<(), Output = f64>,
Contrast: Node<(), Output = f64>,
{
pub fn new(brightness: Brightness, contrast: Contrast) -> Self {
Self { brightness, contrast }
}
}
// https://www.dfstudios.co.uk/articles/programming/image-programming-algorithms/image-processing-algorithms-part-6-gamma-correction/
fn image_gamma(mut image: Image, gamma: f32) -> Image {
let inverse_gamma = 1. / gamma;
let channel = |channel: f32| channel.powf(inverse_gamma);
for pixel in &mut image.data {
*pixel = Color::from_rgbaf32_unchecked(channel(pixel.r()), channel(pixel.g()), channel(pixel.b()), pixel.a())
}
image
}
#[derive(Debug, Clone, Copy)]
pub struct ImageGammaNode<N: Node<(), Output = f64>>(N);
impl<N: Node<(), Output = f64>> Node<Image> for ImageGammaNode<N> {
type Output = Image;
fn eval(self, image: Image) -> Image {
image_gamma(image, self.0.eval(()) as f32)
}
}
impl<N: Node<(), Output = f64> + Copy> Node<Image> for &ImageGammaNode<N> {
type Output = Image;
fn eval(self, image: Image) -> Image {
image_gamma(image, self.0.eval(()) as f32)
}
}
impl<N: Node<(), Output = f64> + Copy> ImageGammaNode<N> {
pub fn new(node: N) -> Self {
Self(node)
}
}
fn image_opacity(mut image: Image, opacity_multiplier: f32) -> Image {
for pixel in &mut image.data {
*pixel = Color::from_rgbaf32_unchecked(pixel.r(), pixel.g(), pixel.b(), pixel.a() * opacity_multiplier)
}
image
}
// Based on http://www.axiomx.com/posterize.htm
fn posterize(mut image: Image, posterize_value: f32) -> Image {
let number_of_areas = posterize_value.recip();
let size_of_areas = (posterize_value - 1.).recip();
let channel = |channel: f32| (channel / number_of_areas).floor() * size_of_areas;
for pixel in &mut image.data {
*pixel = Color::from_rgbaf32_unchecked(channel(pixel.r()), channel(pixel.g()), channel(pixel.b()), pixel.a())
}
image
}
// Based on https://stackoverflow.com/questions/12166117/what-is-the-math-behind-exposure-adjustment-on-photoshop
fn exposure(mut image: Image, exposure: f32) -> Image {
let multiplier = 2f32.powf(exposure);
let channel = |channel: f32| channel * multiplier;
for pixel in &mut image.data {
*pixel = Color::from_rgbaf32_unchecked(channel(pixel.r()), channel(pixel.g()), channel(pixel.b()), pixel.a())
}
image
}
#[derive(Debug, Clone, Copy)]
pub struct Posterize<N: Node<(), Output = f64>>(N);
impl<N: Node<(), Output = f64>> Node<Image> for Posterize<N> {
type Output = Image;
fn eval(self, image: Image) -> Image {
posterize(image, self.0.eval(()) as f32)
}
}
impl<N: Node<(), Output = f64> + Copy> Node<Image> for &Posterize<N> {
type Output = Image;
fn eval(self, image: Image) -> Image {
posterize(image, self.0.eval(()) as f32)
}
}
impl<N: Node<(), Output = f64> + Copy> Posterize<N> {
pub fn new(node: N) -> Self {
Self(node)
}
}
#[derive(Debug, Clone, Copy)]
pub struct HueShiftImage<N: Node<(), Output = f32>>(N);
pub struct ImageOpacityNode<N: Node<(), Output = f64>>(N);
impl<N: Node<(), Output = f32>> Node<Image> for HueShiftImage<N> {
impl<N: Node<(), Output = f64>> Node<Image> for ImageOpacityNode<N> {
type Output = Image;
fn eval(self, mut image: Image) -> Image {
let hue_shift = self.0.eval(());
for pixel in &mut image.data {
let [hue, saturation, luminance, alpha] = pixel.to_hsla();
*pixel = Color::from_hsla(hue + hue_shift / 360., saturation, luminance, alpha);
}
image
fn eval(self, image: Image) -> Image {
image_opacity(image, self.0.eval(()) as f32)
}
}
impl<N: Node<(), Output = f32> + Copy> Node<Image> for &HueShiftImage<N> {
impl<N: Node<(), Output = f64> + Copy> Node<Image> for &ImageOpacityNode<N> {
type Output = Image;
fn eval(self, mut image: Image) -> Image {
let hue_shift = self.0.eval(());
for pixel in &mut image.data {
let [hue, saturation, luminance, alpha] = pixel.to_hsla();
*pixel = Color::from_hsla(hue + hue_shift / 360., saturation, luminance, alpha);
}
image
fn eval(self, image: Image) -> Image {
image_opacity(image, self.0.eval(()) as f32)
}
}
impl<N: Node<(), Output = f32> + Copy> HueShiftImage<N> {
impl<N: Node<(), Output = f64> + Copy> ImageOpacityNode<N> {
pub fn new(node: N) -> Self {
Self(node)
}
}
#[derive(Debug, Clone, Copy)]
pub struct ExposureNode<N: Node<(), Output = f64>>(N);
impl<N: Node<(), Output = f64>> Node<Image> for ExposureNode<N> {
type Output = Image;
fn eval(self, image: Image) -> Image {
exposure(image, self.0.eval(()) as f32)
}
}
impl<N: Node<(), Output = f64> + Copy> Node<Image> for &ExposureNode<N> {
type Output = Image;
fn eval(self, image: Image) -> Image {
exposure(image, self.0.eval(()) as f32)
}
}
impl<N: Node<(), Output = f64> + Copy> ExposureNode<N> {
pub fn new(node: N) -> Self {
Self(node)
}