@@ -268,180 +268,251 @@ impl From<BlendMode> for vello::peniko::Mix {
268
268
}
269
269
270
270
#[ 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
280
289
}
281
290
282
291
#[ 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
291
309
}
292
310
293
311
#[ 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
299
325
}
300
326
301
327
// From https://stackoverflow.com/questions/39510072/algorithm-for-adjustment-of-image-levels
302
328
#[ 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
+ } ;
332
369
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. ) ) ;
336
373
337
- // Midtones (Range: 0-1)
338
- let color = color. gamma ( gamma) ;
374
+ // Midtones (Range: 0-1)
375
+ let color = color. gamma ( gamma) ;
339
376
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) ;
342
379
343
- color. to_linear_srgb ( )
380
+ color. to_linear_srgb ( )
381
+ } ) ;
382
+ input
344
383
}
345
384
346
385
// From <https://stackoverflow.com/a/55233732/775283>
347
386
// Works the same for gamma and linear color
348
387
#[ 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 ( ) ;
376
403
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;
378
430
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) ;
381
433
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 ( ) ;
383
435
384
- color. to_linear_srgb ( )
436
+ color. to_linear_srgb ( )
437
+ } ) ;
438
+ input
385
439
}
386
440
387
441
#[ 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 ( ) ;
390
453
391
- let [ hue, saturation, lightness, alpha] = color. to_hsla ( ) ;
454
+ let [ hue, saturation, lightness, alpha] = color. to_hsla ( ) ;
392
455
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
+ ) ;
401
464
402
- color. to_linear_srgb ( )
465
+ color. to_linear_srgb ( )
466
+ } ) ;
467
+ input
403
468
}
404
469
405
470
#[ 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| {
419
478
let color = color. to_gamma_srgb ( ) ;
420
479
421
480
let color = color. map_rgb ( |c| color. a ( ) - c) ;
422
481
423
482
color. to_linear_srgb ( )
424
- }
483
+ } ) ;
484
+ input
425
485
}
426
486
427
487
#[ 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
+ } ;
439
508
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
445
516
}
446
517
447
518
trait Blend < P : Pixel > {
0 commit comments