Skip to content

Commit 13f1a90

Browse files
committed
Fix node registry types
1 parent 37263f2 commit 13f1a90

File tree

2 files changed

+206
-172
lines changed

2 files changed

+206
-172
lines changed

node-graph/gcore/src/raster/adjustments.rs

Lines changed: 204 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -268,180 +268,251 @@ impl From<BlendMode> for vello::peniko::Mix {
268268
}
269269

270270
#[node_macro::new_node_fn(category("Adjustments"))]
271-
fn luminance_color_node(color: Color, luminance_calc: LuminanceCalculation) -> Color {
272-
let luminance = match luminance_calc {
273-
LuminanceCalculation::SRGB => color.luminance_srgb(),
274-
LuminanceCalculation::Perceptual => color.luminance_perceptual(),
275-
LuminanceCalculation::AverageChannels => color.average_rgb_channels(),
276-
LuminanceCalculation::MinimumChannels => color.minimum_rgb_channels(),
277-
LuminanceCalculation::MaximumChannels => color.maximum_rgb_channels(),
278-
};
279-
color.map_rgb(|_| luminance)
271+
fn luminance<T: Adjust<Color>>(
272+
_: (),
273+
#[expose]
274+
#[implementations(Color, ImageFrame<Color>)]
275+
mut input: T,
276+
luminance_calc: LuminanceCalculation,
277+
) -> T {
278+
input.adjust(|color| {
279+
let luminance = match luminance_calc {
280+
LuminanceCalculation::SRGB => color.luminance_srgb(),
281+
LuminanceCalculation::Perceptual => color.luminance_perceptual(),
282+
LuminanceCalculation::AverageChannels => color.average_rgb_channels(),
283+
LuminanceCalculation::MinimumChannels => color.minimum_rgb_channels(),
284+
LuminanceCalculation::MaximumChannels => color.maximum_rgb_channels(),
285+
};
286+
color.map_rgb(|_| luminance)
287+
});
288+
input
280289
}
281290

282291
#[node_macro::new_node_fn(category("Adjustments"))]
283-
fn extract_channel_node(color: Color, channel: RedGreenBlueAlpha) -> Color {
284-
let extracted_value = match channel {
285-
RedGreenBlueAlpha::Red => color.r(),
286-
RedGreenBlueAlpha::Green => color.g(),
287-
RedGreenBlueAlpha::Blue => color.b(),
288-
RedGreenBlueAlpha::Alpha => color.a(),
289-
};
290-
color.map_rgba(|_| extracted_value)
292+
fn extract_channel<T: Adjust<Color>>(
293+
_: (),
294+
#[expose]
295+
#[implementations(Color, ImageFrame<Color>)]
296+
mut input: T,
297+
channel: RedGreenBlueAlpha,
298+
) -> T {
299+
input.adjust(|color| {
300+
let extracted_value = match channel {
301+
RedGreenBlueAlpha::Red => color.r(),
302+
RedGreenBlueAlpha::Green => color.g(),
303+
RedGreenBlueAlpha::Blue => color.b(),
304+
RedGreenBlueAlpha::Alpha => color.a(),
305+
};
306+
color.map_rgba(|_| extracted_value)
307+
});
308+
input
291309
}
292310

293311
#[node_macro::new_node_fn(category("Adjustments"))]
294-
fn extract_opaque_node(color: Color) -> Color {
295-
if color.a() == 0. {
296-
return color.with_alpha(1.);
297-
}
298-
Color::from_rgbaf32(color.r() / color.a(), color.g() / color.a(), color.b() / color.a(), 1.).unwrap()
312+
fn extract_opaque<T: Adjust<Color>>(
313+
_: (),
314+
#[expose]
315+
#[implementations(Color, ImageFrame<Color>)]
316+
mut input: T,
317+
) -> T {
318+
input.adjust(|color| {
319+
if color.a() == 0. {
320+
return color.with_alpha(1.);
321+
}
322+
Color::from_rgbaf32(color.r() / color.a(), color.g() / color.a(), color.b() / color.a(), 1.).unwrap()
323+
});
324+
input
299325
}
300326

301327
// From https://stackoverflow.com/questions/39510072/algorithm-for-adjustment-of-image-levels
302328
#[node_macro::new_node_fn(category("Adjustments"))]
303-
fn levels_node(color: Color, input_start: f64, input_mid: f64, input_end: f64, output_start: f64, output_end: f64) -> Color {
304-
let color = color.to_gamma_srgb();
305-
306-
// Input Range (Range: 0-1)
307-
let input_shadows = (input_start / 100.) as f32;
308-
let input_midtones = (input_mid / 100.) as f32;
309-
let input_highlights = (input_end / 100.) as f32;
310-
311-
// Output Range (Range: 0-1)
312-
let output_minimums = (output_start / 100.) as f32;
313-
let output_maximums = (output_end / 100.) as f32;
314-
315-
// Midtones interpolation factor between minimums and maximums (Range: 0-1)
316-
let midtones = output_minimums + (output_maximums - output_minimums) * input_midtones;
317-
318-
// Gamma correction (Range: 0.01-10)
319-
let gamma = if midtones < 0.5 {
320-
// Range: 0-1
321-
let x = 1. - midtones * 2.;
322-
// Range: 1-10
323-
1. + 9. * x
324-
} else {
325-
// Range: 0-0.5
326-
let x = 1. - midtones;
327-
// Range: 0-1
328-
let x = x * 2.;
329-
// Range: 0.01-1
330-
x.max(0.01)
331-
};
329+
fn levels<T: Adjust<Color>>(
330+
_: (),
331+
#[expose]
332+
#[implementations(Color, ImageFrame<Color>)]
333+
mut input: T,
334+
input_start: f64,
335+
input_mid: f64,
336+
input_end: f64,
337+
output_start: f64,
338+
output_end: f64,
339+
) -> T {
340+
input.adjust(|color| {
341+
let color = color.to_gamma_srgb();
342+
343+
// Input Range (Range: 0-1)
344+
let input_shadows = (input_start / 100.) as f32;
345+
let input_midtones = (input_mid / 100.) as f32;
346+
let input_highlights = (input_end / 100.) as f32;
347+
348+
// Output Range (Range: 0-1)
349+
let output_minimums = (output_start / 100.) as f32;
350+
let output_maximums = (output_end / 100.) as f32;
351+
352+
// Midtones interpolation factor between minimums and maximums (Range: 0-1)
353+
let midtones = output_minimums + (output_maximums - output_minimums) * input_midtones;
354+
355+
// Gamma correction (Range: 0.01-10)
356+
let gamma = if midtones < 0.5 {
357+
// Range: 0-1
358+
let x = 1. - midtones * 2.;
359+
// Range: 1-10
360+
1. + 9. * x
361+
} else {
362+
// Range: 0-0.5
363+
let x = 1. - midtones;
364+
// Range: 0-1
365+
let x = x * 2.;
366+
// Range: 0.01-1
367+
x.max(0.01)
368+
};
332369

333-
// Input levels (Range: 0-1)
334-
let highlights_minus_shadows = (input_highlights - input_shadows).clamp(f32::EPSILON, 1.);
335-
let color = color.map_rgb(|c| ((c - input_shadows).max(0.) / highlights_minus_shadows).min(1.));
370+
// Input levels (Range: 0-1)
371+
let highlights_minus_shadows = (input_highlights - input_shadows).clamp(f32::EPSILON, 1.);
372+
let color = color.map_rgb(|c| ((c - input_shadows).max(0.) / highlights_minus_shadows).min(1.));
336373

337-
// Midtones (Range: 0-1)
338-
let color = color.gamma(gamma);
374+
// Midtones (Range: 0-1)
375+
let color = color.gamma(gamma);
339376

340-
// Output levels (Range: 0-1)
341-
let color = color.map_rgb(|c| c * (output_maximums - output_minimums) + output_minimums);
377+
// Output levels (Range: 0-1)
378+
let color = color.map_rgb(|c| c * (output_maximums - output_minimums) + output_minimums);
342379

343-
color.to_linear_srgb()
380+
color.to_linear_srgb()
381+
});
382+
input
344383
}
345384

346385
// From <https://stackoverflow.com/a/55233732/775283>
347386
// Works the same for gamma and linear color
348387
#[node_macro::new_node_fn(category("Adjustments"))]
349-
fn black_and_white_color_node(color: Color, tint: Color, reds: f64, yellows: f64, greens: f64, cyans: f64, blues: f64, magentas: f64) -> Color {
350-
let color = color.to_gamma_srgb();
351-
352-
let reds = reds as f32 / 100.;
353-
let yellows = yellows as f32 / 100.;
354-
let greens = greens as f32 / 100.;
355-
let cyans = cyans as f32 / 100.;
356-
let blues = blues as f32 / 100.;
357-
let magentas = magentas as f32 / 100.;
358-
359-
let gray_base = color.r().min(color.g()).min(color.b());
360-
361-
let red_part = color.r() - gray_base;
362-
let green_part = color.g() - gray_base;
363-
let blue_part = color.b() - gray_base;
364-
let alpha_part = color.a();
365-
366-
let additional = if red_part == 0. {
367-
let cyan_part = green_part.min(blue_part);
368-
cyan_part * cyans + (green_part - cyan_part) * greens + (blue_part - cyan_part) * blues
369-
} else if green_part == 0. {
370-
let magenta_part = red_part.min(blue_part);
371-
magenta_part * magentas + (red_part - magenta_part) * reds + (blue_part - magenta_part) * blues
372-
} else {
373-
let yellow_part = red_part.min(green_part);
374-
yellow_part * yellows + (red_part - yellow_part) * reds + (green_part - yellow_part) * greens
375-
};
388+
fn black_and_white_color<T: Adjust<Color>>(
389+
_: (),
390+
#[expose]
391+
#[implementations(Color, ImageFrame<Color>)]
392+
mut input: T,
393+
tint: Color,
394+
reds: f64,
395+
yellows: f64,
396+
greens: f64,
397+
cyans: f64,
398+
blues: f64,
399+
magentas: f64,
400+
) -> T {
401+
input.adjust(|color| {
402+
let color = color.to_gamma_srgb();
376403

377-
let luminance = gray_base + additional;
404+
let reds = reds as f32 / 100.;
405+
let yellows = yellows as f32 / 100.;
406+
let greens = greens as f32 / 100.;
407+
let cyans = cyans as f32 / 100.;
408+
let blues = blues as f32 / 100.;
409+
let magentas = magentas as f32 / 100.;
410+
411+
let gray_base = color.r().min(color.g()).min(color.b());
412+
413+
let red_part = color.r() - gray_base;
414+
let green_part = color.g() - gray_base;
415+
let blue_part = color.b() - gray_base;
416+
let alpha_part = color.a();
417+
418+
let additional = if red_part == 0. {
419+
let cyan_part = green_part.min(blue_part);
420+
cyan_part * cyans + (green_part - cyan_part) * greens + (blue_part - cyan_part) * blues
421+
} else if green_part == 0. {
422+
let magenta_part = red_part.min(blue_part);
423+
magenta_part * magentas + (red_part - magenta_part) * reds + (blue_part - magenta_part) * blues
424+
} else {
425+
let yellow_part = red_part.min(green_part);
426+
yellow_part * yellows + (red_part - yellow_part) * reds + (green_part - yellow_part) * greens
427+
};
428+
429+
let luminance = gray_base + additional;
378430

379-
// TODO: Fix "Color" blend mode implementation so it matches the expected behavior perfectly (it's currently close)
380-
let color = tint.with_luminance(luminance);
431+
// TODO: Fix "Color" blend mode implementation so it matches the expected behavior perfectly (it's currently close)
432+
let color = tint.with_luminance(luminance);
381433

382-
let color = Color::from_rgbaf32(color.r(), color.g(), color.b(), alpha_part).unwrap();
434+
let color = Color::from_rgbaf32(color.r(), color.g(), color.b(), alpha_part).unwrap();
383435

384-
color.to_linear_srgb()
436+
color.to_linear_srgb()
437+
});
438+
input
385439
}
386440

387441
#[node_macro::new_node_fn(category("Adjustments"))]
388-
fn hue_shift(color: Color, hue_shift: f64, saturation_shift: f64, lightness_shift: f64) -> Color {
389-
let color = color.to_gamma_srgb();
442+
fn hue_shift<T: Adjust<Color>>(
443+
_: (),
444+
#[expose]
445+
#[implementations(Color, ImageFrame<Color>)]
446+
mut input: T,
447+
hue_shift: f64,
448+
saturation_shift: f64,
449+
lightness_shift: f64,
450+
) -> T {
451+
input.adjust(|color| {
452+
let color = color.to_gamma_srgb();
390453

391-
let [hue, saturation, lightness, alpha] = color.to_hsla();
454+
let [hue, saturation, lightness, alpha] = color.to_hsla();
392455

393-
let color = Color::from_hsla(
394-
(hue + hue_shift as f32 / 360.) % 1.,
395-
// TODO: Improve the way saturation works (it's slightly off)
396-
(saturation + saturation_shift as f32 / 100.).clamp(0., 1.),
397-
// TODO: Fix the way lightness works (it's very off)
398-
(lightness + lightness_shift as f32 / 100.).clamp(0., 1.),
399-
alpha,
400-
);
456+
let color = Color::from_hsla(
457+
(hue + hue_shift as f32 / 360.) % 1.,
458+
// TODO: Improve the way saturation works (it's slightly off)
459+
(saturation + saturation_shift as f32 / 100.).clamp(0., 1.),
460+
// TODO: Fix the way lightness works (it's very off)
461+
(lightness + lightness_shift as f32 / 100.).clamp(0., 1.),
462+
alpha,
463+
);
401464

402-
color.to_linear_srgb()
465+
color.to_linear_srgb()
466+
});
467+
input
403468
}
404469

405470
#[node_macro::new_node_fn(category("Adjustments"))]
406-
fn invert(color: Color) -> Color {
407-
let color = color.to_gamma_srgb();
408-
409-
let color = color.map_rgb(|c| color.a() - c);
410-
411-
color.to_linear_srgb()
412-
}
413-
414-
// TODO replace with trait based implementation
415-
impl<'i> Node<'i, &'i Color> for InvertNode {
416-
type Output = Color;
417-
418-
fn eval(&'i self, color: &'i Color) -> Self::Output {
471+
fn invert<T: Adjust<Color>>(
472+
_: (),
473+
#[expose]
474+
#[implementations(Color, ImageFrame<Color>)]
475+
mut input: T,
476+
) -> T {
477+
input.adjust(|color| {
419478
let color = color.to_gamma_srgb();
420479

421480
let color = color.map_rgb(|c| color.a() - c);
422481

423482
color.to_linear_srgb()
424-
}
483+
});
484+
input
425485
}
426486

427487
#[node_macro::new_node_fn(category("Adjustments"))]
428-
fn threshold_node(color: Color, min_luminance: f64, max_luminance: f64, luminance_calc: LuminanceCalculation) -> Color {
429-
let min_luminance = Color::srgb_to_linear(min_luminance as f32 / 100.);
430-
let max_luminance = Color::srgb_to_linear(max_luminance as f32 / 100.);
431-
432-
let luminance = match luminance_calc {
433-
LuminanceCalculation::SRGB => color.luminance_srgb(),
434-
LuminanceCalculation::Perceptual => color.luminance_perceptual(),
435-
LuminanceCalculation::AverageChannels => color.average_rgb_channels(),
436-
LuminanceCalculation::MinimumChannels => color.minimum_rgb_channels(),
437-
LuminanceCalculation::MaximumChannels => color.maximum_rgb_channels(),
438-
};
488+
fn threshold<T: Adjust<Color>>(
489+
_: (),
490+
#[expose]
491+
#[implementations(Color, ImageFrame<Color>)]
492+
mut input: T,
493+
min_luminance: f64,
494+
max_luminance: f64,
495+
luminance_calc: LuminanceCalculation,
496+
) -> T {
497+
input.adjust(|color| {
498+
let min_luminance = Color::srgb_to_linear(min_luminance as f32 / 100.);
499+
let max_luminance = Color::srgb_to_linear(max_luminance as f32 / 100.);
500+
501+
let luminance = match luminance_calc {
502+
LuminanceCalculation::SRGB => color.luminance_srgb(),
503+
LuminanceCalculation::Perceptual => color.luminance_perceptual(),
504+
LuminanceCalculation::AverageChannels => color.average_rgb_channels(),
505+
LuminanceCalculation::MinimumChannels => color.minimum_rgb_channels(),
506+
LuminanceCalculation::MaximumChannels => color.maximum_rgb_channels(),
507+
};
439508

440-
if luminance >= min_luminance && luminance <= max_luminance {
441-
Color::WHITE
442-
} else {
443-
Color::BLACK
444-
}
509+
if luminance >= min_luminance && luminance <= max_luminance {
510+
Color::WHITE
511+
} else {
512+
Color::BLACK
513+
}
514+
});
515+
input
445516
}
446517

447518
trait Blend<P: Pixel> {

0 commit comments

Comments
 (0)