ANimator120
ANimator120

Reputation: 3401

Piping an Image Through a Non Static Array of Functions in Rust

I'm trying to re a Photoshop Adjustment Layer style process where image processing functions are representing by layers in a stack, where the result of the top/first process is fed into the next process. This could be represented as an array of processing functions, where the output of each function is fed into the next.

[Change Brightness(img)->Change Saturation(img)->Change Levels(img)]

How could I achieve this kind of 'handing off' of a value from a nonstatic array of functions in Rust?

fn op_a(mut a: Vec<u8>, p1: i32) -> Vec<u8> {
    //process image
    println!("Operation A on image with size: {}, and parameter1 val: {}", a.len(), p1);
    a[0] = 10;
    a
}

fn op_b(mut a: Vec<u8>, p1: i32, p2: i32) -> Vec<u8> {
    //process image
    println!("Operation B on image with size: {}, parameter 1 val: {}, parameter 2 val: {}", a.len(), p1, p2);
    a[0] += 2;
    a
}

fn op_c(mut a: Vec<u8>, p1: i32, p2: i32, p3: i32) -> Vec<u8> {
    //process image
    println!("Operation C on image with size: {}, parameter 1 val: {}, parameter 2 val: {}, parameter 3 val: {}", a.len(), p1, p2, p3);
    a[0] *= 2;
    a
}

fn pipe_function_array(initial_image: ImageBuffer, Vec<functions>)->ImageBuffer{
        //executes an array of functions passing each to the next function in the array, with an initual input value of initial_image
    
}

fn main() {
    let my_starting_value =  *some Vec<u8>*;
    let my_funcs = *an array of op_a, op_b, and op_c*;
    let final_image = pipe_function_array(vec![0], my_funcs);
}

ps adjustment layers

Upvotes: 1

Views: 100

Answers (1)

8176135
8176135

Reputation: 4133

Your code basically works once you fix the obvious syntax errors.

fn op_a(mut a: Vec<u8>) -> Vec<u8> {
    //process image
    println!("Operation A on image with size: {}", a.len());
    a[0] = 10;
    a
}

fn op_b(mut a: Vec<u8>) -> Vec<u8> {
    //process image
    println!("Operation B on image with size: {}", a.len());
    a[0] += 2;
    a
}

fn op_c(mut a: Vec<u8>) -> Vec<u8> {
    //process image
    println!("Operation C on image with size: {}", a.len());
    a[0] *= 2;
    a
}

fn pipe_function_array(initial_image: Vec<u8>, a: Vec<fn(Vec<u8>) -> Vec<u8>>) -> Vec<u8> {
    //Using fold here, but you can easily just use a for loop if you prefer.
    a.iter()
        .fold(initial_image, |image, operation| (operation)(image))
}

fn main() {
    // You have to define the type of the Vec here, 
    // otherwise the compiler will throw some ambiguous errors.
    let my_funcs: Vec<fn(Vec<u8>) -> Vec<u8>> = vec![op_a, op_b, op_c];

    let final_image = pipe_function_array(vec![0], my_funcs);
    dbg!(final_image);
}

Playground


Instead of directly passing function pointers however, I would suggest taking a different approach of using an array of enums.

enum Operations {
    Saturation {
        config: SaturationConfig,
    },
    Contrast {
        config: ContrastConfig,
    }
}

fn saturation(mut a: Vec<u8>, config: SaturationConfig) -> Vec<u8> {
    //process image
    a
}
fn contrast(mut a: Vec<u8>, config: ContrastConfig) -> Vec<u8> {
    //process image
    a
}

fn pipe_function_array(mut initial_image: Vec<u8>, a: Vec<Operations>) -> Vec<u8> {
    a.iter()
        .fold(initial_image, |image, operation| match operation {
            Operations::Saturation { config } => saturation(image, config),
            Operations::Contrast { config } => contrast(image, config),
        })
}

Upvotes: 1

Related Questions