From e639bf5d9ee92f69aa967be43f4b909086d78d04 Mon Sep 17 00:00:00 2001 From: Jasyd Caballero Date: Thu, 5 Nov 2026 18:34:31 -0500 Subject: [PATCH 1/4] rebase from main --- crates/core/src/tests.rs | 2 +- crates/engine/src/document.rs | 8 ++++++-- crates/engine/src/formats.rs | 2 ++ crates/engine/src/native.rs | 5 +++++ crates/engine/src/studio.rs | 4 ++-- crates/gpu/src/canvas.rs | 9 +++++++-- crates/gpu/src/shaders/blend.wgsl | 3 +++ crates/gpu/src/shaders/composite.wgsl | 2 +- 8 files changed, 27 insertions(+), 8 deletions(-) diff --git a/crates/core/src/tests.rs b/crates/core/src/tests.rs index 271ddb00..15e8a1f3 100644 --- a/crates/core/src/tests.rs +++ b/crates/core/src/tests.rs @@ -773,7 +773,7 @@ fn merge_duplicate_blend_and_locks() { assert_eq!(s.layers().len(), 2); assert!(s.set_layer_blend(copy, procraft_engine::document::BlendMode::Multiply.id())); assert!(!s.set_layer_blend(copy, 999)); - assert_eq!(crate::layers::blend_modes().len(), 27); + assert_eq!(crate::layers::blend_modes().len(), 28); assert!(s.merge_down(copy)); assert_eq!(s.layers().len(), 1); // red multiplied by red is still red diff --git a/crates/engine/src/document.rs b/crates/engine/src/document.rs index b041638c..9c7fc325 100644 --- a/crates/engine/src/document.rs +++ b/crates/engine/src/document.rs @@ -38,12 +38,13 @@ pub enum BlendMode { Saturation, Color, Luminosity, + Greater, } impl BlendMode { /// Every mode, in the order the layer panel lists them (grouped: darken, normal, lighten, /// contrast, difference, colour). - pub const ALL: [BlendMode; 27] = [ + pub const ALL: [BlendMode; 28] = [ BlendMode::Multiply, BlendMode::Darken, BlendMode::Shade, @@ -71,10 +72,11 @@ impl BlendMode { BlendMode::Saturation, BlendMode::Color, BlendMode::Luminosity, + BlendMode::Greater, ]; /// The highest of the modes' numbers. - pub const MAX_ID: u32 = 26; + pub const MAX_ID: u32 = 30; /// The renderer's number for the mode (stable: shaders switch on it). pub fn id(self) -> u32 { @@ -106,6 +108,7 @@ impl BlendMode { BlendMode::Color => 24, BlendMode::Luminosity => 25, BlendMode::Shade => 26, + BlendMode::Greater => 30, } } @@ -142,6 +145,7 @@ impl BlendMode { BlendMode::Saturation => "Saturation", BlendMode::Color => "Color", BlendMode::Luminosity => "Luminosity", + BlendMode::Greater => "Greater", } } } diff --git a/crates/engine/src/formats.rs b/crates/engine/src/formats.rs index fba68aff..f0239657 100644 --- a/crates/engine/src/formats.rs +++ b/crates/engine/src/formats.rs @@ -407,6 +407,8 @@ fn blend_key(mode: BlendMode) -> &'static [u8; 4] { BlendMode::Saturation => b"sat ", BlendMode::Color => b"colr", BlendMode::Luminosity => b"lum ", + // procreate has not "greater", "normal" is the nearest + BlendMode::Greater => b"norm", } } diff --git a/crates/engine/src/native.rs b/crates/engine/src/native.rs index 2f82859e..0c680c4a 100644 --- a/crates/engine/src/native.rs +++ b/crates/engine/src/native.rs @@ -1484,6 +1484,11 @@ mod tests { #[test] fn every_blend_mode_has_its_number() { for m in BlendMode::ALL { + // Procreate has no Greater mode: it saves as Normal + if m == BlendMode::Greater { + assert_eq!(blend_from(blend_to(m)), BlendMode::Normal); + continue; + } assert_eq!(blend_from(blend_to(m)), m, "{m:?}"); } assert_eq!( diff --git a/crates/engine/src/studio.rs b/crates/engine/src/studio.rs index bf6acd4a..411d3e03 100644 --- a/crates/engine/src/studio.rs +++ b/crates/engine/src/studio.rs @@ -318,8 +318,8 @@ impl Brush { // blend modes are kept by number (as the layers' are numbered; a grain's own and behind // past them) "blend_mode" | "burnt_blend" => { - let id = value.round().clamp(0.0, BRUSH_BEHIND as f32); - let known = id <= BlendMode::MAX_ID as f32 || id == BRUSH_BEHIND as f32; + let id = value.round().clamp(0.0, BlendMode::MAX_ID as f32); + let known = BlendMode::from_id(id as u32).is_some() || id == BRUSH_BEHIND as f32; self.extra.insert(key.to_owned(), if known { id } else { 0.0 }); } // the grain's and a dual brush's second's: a layer mode or the grain's own heights diff --git a/crates/gpu/src/canvas.rs b/crates/gpu/src/canvas.rs index f51bb8c8..bf6ac8c4 100644 --- a/crates/gpu/src/canvas.rs +++ b/crates/gpu/src/canvas.rs @@ -260,6 +260,11 @@ impl StrokeStyle { } } + fn brush_blend(value: f32) -> u32 { + let id = value.round().clamp(0.0, BlendMode::MAX_ID as f32) as u32; + if BlendMode::from_id(id).is_some() || id == BRUSH_BEHIND { id } else { 0 } + } + /// A stroke drawn with `brush`'s shape, grain and rendering. pub fn for_brush( brush: &Brush, @@ -303,10 +308,10 @@ impl StrokeStyle { grade: if wet { brush.setting("wet_grade").clamp(-1.0, 1.0) } else { 0.0 }, blur: if wet { brush.setting("wet_blur").clamp(0.0, 1.0) } else { 0.0 }, wet_jitter: if wet { brush.setting("wet_jitter").clamp(0.0, 1.0) } else { 0.0 }, - blend: brush.setting("blend_mode").round().clamp(0.0, BRUSH_BEHIND as f32) as u32, + blend: Self::brush_blend(brush.setting("blend_mode")), blur_jitter: if wet { brush.setting("wet_blur_jitter").clamp(0.0, 1.0) } else { 0.0 }, grain_blend: brush.setting("grain_blend").round().clamp(0.0, GRAIN_LINEAR_HEIGHT as f32) as u32, - burnt_blend: brush.setting("burnt_blend").round().clamp(0.0, BRUSH_BEHIND as f32) as u32, + burnt_blend: Self::brush_blend(brush.setting("burnt_blend")), // a brush's rotation turns its grain the other way to the shaders' angle // the editors' turn of the image, the whole grain with it (the rotation control is // each stamp's own: `Stamp::grain_turn`) diff --git a/crates/gpu/src/shaders/blend.wgsl b/crates/gpu/src/shaders/blend.wgsl index 685f34b3..736bcf1b 100644 --- a/crates/gpu/src/shaders/blend.wgsl +++ b/crates/gpu/src/shaders/blend.wgsl @@ -220,6 +220,9 @@ fn blend(b: vec4, s: vec4, mode: u32) -> vec4 { if (mode == 29u) { return b + s * (1.0 - b.a); } + if (mode == 30u) { + return select(b, s, s.a >= b.a); + } let cb = unpremultiply(b); let cs = unpremultiply(s); let mixed = clamp(blend_rgb(cb, cs, mode), vec3(0.0), vec3(1.0)); diff --git a/crates/gpu/src/shaders/composite.wgsl b/crates/gpu/src/shaders/composite.wgsl index 8e1062ba..4a9ff085 100644 --- a/crates/gpu/src/shaders/composite.wgsl +++ b/crates/gpu/src/shaders/composite.wgsl @@ -275,7 +275,7 @@ fn unsquared(c: vec4) -> vec4 { // the layer's) fn stroke_into(b: vec4, s: vec4) -> vec4 { let mode = lu.stroke_blend & 255u; - if ((lu.stroke_blend & STROKE_LUMINANCE) == 0u || mode == 26u || mode == 29u || s.a <= 0.0) { + if ((lu.stroke_blend & STROKE_LUMINANCE) == 0u || mode == 26u || mode == 29u || mode == 30u || s.a <= 0.0) { return blend(b, s, mode); } let cb = unpremultiply(b); From d726146591899e2e9c29bdec825b95c8d337fba2 Mon Sep 17 00:00:00 2001 From: Jasyd Caballero Date: Mon, 2 Nov 2026 22:36:27 -0500 Subject: [PATCH 2/4] add icon --- .../java/io/github/artcraftandroid/procraft/ui/Icons.kt | 1 + assets/icons/procraft/blend-greater.svg | 4 ++++ assets/icons/slots.tsv | 1 + desktop/src/icons.rs | 3 +++ docs/icons/STYLE_GUIDE.md | 3 ++- tools/icons/draw_icons.py | 9 ++++++++- 6 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 assets/icons/procraft/blend-greater.svg diff --git a/android/app/src/main/java/io/github/artcraftandroid/procraft/ui/Icons.kt b/android/app/src/main/java/io/github/artcraftandroid/procraft/ui/Icons.kt index 1bc1beef..983e4475 100644 --- a/android/app/src/main/java/io/github/artcraftandroid/procraft/ui/Icons.kt +++ b/android/app/src/main/java/io/github/artcraftandroid/procraft/ui/Icons.kt @@ -89,6 +89,7 @@ object PcIcons { "Linear Burn" to ours("blend-linear-burn", 26f, "M14.35 6.44L14.27 6.49L12.12 10.46L12.03 10.64L11.91 11.02L11.85 11.41L11.84 11.61L11.87 12L11.96 12.39L12.03 12.57L12.21 12.93L12.32 13.09L12.58 13.4L12.88 13.65L13.22 13.86L13.4 13.94L13.78 14.07L14.17 14.13L14.37 14.14L14.57 14.13L14.96 14.07L15.34 13.94L15.52 13.86L15.86 13.65L16.16 13.4L16.42 13.09L16.62 12.76L16.71 12.57L16.83 12.2L16.89 11.81L16.9 11.61L16.89 11.41L16.83 11.02L16.78 10.82L16.62 10.46L14.47 6.49L14.42 6.45ZM14.39 10.4L14.47 10.45L15.25 12.04L15.33 12.26L15.36 12.49L15.35 12.64L15.28 12.87L15.17 13.07L15.07 13.19L14.95 13.29L14.75 13.4L14.6 13.45L14.37 13.48L14.22 13.46L13.99 13.4L13.79 13.29L13.67 13.19L13.57 13.07L13.46 12.87L13.41 12.72L13.38 12.49L13.41 12.26L13.49 12.04L14.27 10.45L14.32 10.41ZM18.69 16.23L18.84 16.13L18.95 16L19.01 15.89L19.04 15.78L19.04 15.6L18.99 15.43L18.93 15.33L18.85 15.24L18.71 15.13L18.54 15.08L18.42 15.07L18.3 15.09L14.37 15.99L10.44 15.09L10.32 15.07L10.2 15.08L10.09 15.11L9.93 15.2L9.81 15.33L9.73 15.48L9.7 15.6L9.7 15.78L9.76 15.95L9.86 16.09L9.95 16.17L10.11 16.25L11.67 16.61L10.16 16.96L10 17.02L9.86 17.13L9.76 17.28L9.7 17.45L9.7 17.62L9.73 17.74L9.78 17.85L9.84 17.94L9.93 18.03L10.03 18.09L10.14 18.13L10.26 18.15L10.38 18.15L14.37 17.23L18.3 18.14L18.42 18.15L18.54 18.14L18.71 18.09L18.85 17.99L18.93 17.9L19.01 17.74L19.04 17.62L19.04 17.45L18.98 17.28L18.88 17.13L18.79 17.05L18.63 16.97L17.07 16.61Z"), "Darker Color" to ours("blend-darker-color", 26f, "M21.73 6.38L21.63 6.03L21.45 5.7L21.21 5.41L20.92 5.17L20.59 4.99L20.24 4.89L19.87 4.85L7.67 4.85L7.3 4.89L6.95 4.99L6.62 5.17L6.33 5.41L6.09 5.7L5.91 6.03L5.81 6.38L5.77 6.75L5.77 18.95L5.81 19.32L5.91 19.67L6.09 20L6.33 20.29L6.62 20.53L6.95 20.71L7.3 20.81L7.67 20.85L19.87 20.85L20.24 20.81L20.59 20.71L20.92 20.53L21.21 20.29L21.45 20L21.63 19.67L21.73 19.32L21.77 18.95L21.77 6.75ZM8.77 11.94L8.57 11.67L8.46 11.36L8.44 11.14L8.46 10.91L8.57 10.6L8.77 10.33L8.94 10.19L9.14 10.08L9.47 10L9.69 10L9.91 10.04L10.12 10.13L10.39 10.33L13.77 13.71L17.15 10.33L17.42 10.13L17.63 10.04L17.96 9.99L18.29 10.04L18.5 10.13L18.77 10.33L18.91 10.5L19.05 10.8L19.1 11.02L19.1 11.25L19.05 11.47L18.91 11.77L18.77 11.94L14.58 16.13L14.4 16.28L14.21 16.38L13.88 16.46L13.66 16.46L13.44 16.42L13.23 16.33L13.04 16.21Z"), "Normal" to ours("blend-normal", 26f, "M21.92 12.8L21.88 12.01L21.76 11.22L21.57 10.45L21.3 9.7L20.96 8.98L20.55 8.3L20.08 7.66L19.55 7.07L18.96 6.54L18.32 6.07L17.64 5.66L16.92 5.32L16.17 5.05L15.4 4.86L14.61 4.74L13.82 4.7L13.03 4.74L12.24 4.86L11.47 5.05L10.72 5.32L10 5.66L9.32 6.07L8.68 6.54L8.09 7.07L7.56 7.66L7.09 8.3L6.68 8.98L6.34 9.7L6.07 10.45L5.88 11.22L5.76 12.01L5.72 12.8L5.76 13.59L5.88 14.38L6.07 15.15L6.34 15.9L6.68 16.62L7.09 17.3L7.56 17.94L8.09 18.53L8.68 19.06L9.32 19.53L10 19.94L10.72 20.28L11.47 20.55L12.24 20.74L13.03 20.86L13.82 20.9L14.61 20.86L15.4 20.74L16.17 20.55L16.92 20.28L17.64 19.94L18.32 19.53L18.96 19.06L19.55 18.53L20.08 17.94L20.55 17.3L20.96 16.62L21.3 15.9L21.57 15.15L21.76 14.38L21.88 13.59Z"), + "Greater" to ours("blend-greater", 26f, "M12.1 7.02L11.53 6.99L10.96 7.02L10.4 7.1L9.84 7.24L9.31 7.43L8.79 7.68L8.3 7.97L7.84 8.31L7.42 8.69L7.04 9.11L6.7 9.57L6.41 10.06L6.16 10.58L5.97 11.11L5.83 11.67L5.75 12.23L5.72 12.8L5.75 13.37L5.83 13.93L5.97 14.49L6.16 15.02L6.41 15.54L6.7 16.03L7.04 16.49L7.42 16.91L7.84 17.29L8.3 17.63L8.79 17.92L9.31 18.17L9.84 18.36L10.4 18.5L10.96 18.58L11.53 18.61L12.1 18.58L12.66 18.5L13.12 18.39L12.6 18.08L12.24 17.82L11.92 17.55L11.38 17.02L10.71 16.94L10.3 16.84L9.91 16.7L9.54 16.53L9.18 16.31L8.85 16.07L8.54 15.79L8.26 15.48L8.02 15.15L7.8 14.79L7.63 14.42L7.49 14.03L7.39 13.62L7.33 13.21L7.3 12.8L7.33 12.39L7.39 11.98L7.49 11.57L7.63 11.18L7.8 10.81L8.02 10.45L8.26 10.12L8.54 9.81L8.85 9.53L9.18 9.29L9.54 9.07L9.91 8.9L10.3 8.76L10.71 8.66L11.38 8.58L11.92 8.05L12.24 7.78L12.6 7.52L13.12 7.21L12.66 7.1ZM21.92 12.8L21.87 12.04L21.72 11.3L21.61 10.93L21.32 10.23L21.14 9.89L20.72 9.26L20.22 8.69L19.94 8.43L19.34 7.97L19.01 7.77L18.68 7.59L17.98 7.3L17.61 7.19L16.87 7.04L16.11 6.99L15.73 7L15.35 7.04L14.61 7.19L13.89 7.43L13.54 7.59L12.88 7.97L12.57 8.19L12 8.69L11.74 8.97L11.5 9.26L11.08 9.89L10.74 10.58L10.61 10.93L10.41 11.67L10.35 12.04L10.3 12.8L10.31 13.18L10.35 13.56L10.5 14.3L10.74 15.02L10.9 15.37L11.28 16.03L11.5 16.34L12 16.91L12.28 17.17L12.88 17.63L13.2 17.83L13.54 18.01L14.24 18.3L14.61 18.41L15.35 18.56L15.73 18.6L16.49 18.6L17.24 18.5L17.61 18.41L17.98 18.3L18.68 18.01L19.01 17.83L19.65 17.41L19.94 17.17L20.48 16.63L20.94 16.03L21.14 15.71L21.48 15.02L21.61 14.67L21.72 14.3L21.87 13.56Z"), "Lighten" to ours("blend-lighten", 26f, "M14.89 3.81L14.76 3.53L14.55 3.3L14.28 3.13L13.97 3.06L13.66 3.07L13.37 3.18L13.11 3.37L12.98 3.53L12.88 3.71L12.82 3.91L12.8 4.12L12.8 6.44L12.82 6.65L12.88 6.85L13.04 7.12L13.19 7.27L13.46 7.43L13.66 7.49L13.87 7.51L14.08 7.49L14.28 7.43L14.46 7.33L14.63 7.2L14.76 7.03L14.86 6.85L14.92 6.65L14.94 6.44L14.94 4.12ZM20.99 7.18L21.1 6.89L21.11 6.58L21.04 6.27L20.94 6.09L20.8 5.92L20.55 5.74L20.26 5.63L19.94 5.62L19.64 5.69L19.45 5.79L19.29 5.92L17.65 7.56L17.52 7.73L17.42 7.91L17.34 8.21L17.34 8.42L17.42 8.73L17.52 8.91L17.65 9.08L17.81 9.21L18 9.31L18.2 9.37L18.41 9.39L18.62 9.37L18.82 9.31L19 9.21L19.16 9.08L20.8 7.44ZM8.2 5.74L7.9 5.63L7.59 5.62L7.28 5.69L7.01 5.85L6.87 6L6.75 6.18L6.64 6.47L6.63 6.79L6.7 7.09L6.8 7.27L6.94 7.44L8.58 9.08L8.74 9.21L8.92 9.31L9.23 9.38L9.44 9.38L9.74 9.31L9.93 9.21L10.09 9.08L10.22 8.91L10.32 8.73L10.38 8.53L10.4 8.32L10.38 8.11L10.32 7.91L10.22 7.73L10.09 7.56L8.45 5.92ZM4.82 11.83L4.54 11.97L4.31 12.18L4.15 12.45L4.07 12.75L4.09 13.07L4.19 13.36L4.38 13.61L4.54 13.75L4.73 13.85L4.93 13.91L5.13 13.93L7.45 13.93L7.66 13.91L7.86 13.85L8.13 13.68L8.34 13.45L8.44 13.27L8.5 13.07L8.52 12.86L8.5 12.65L8.44 12.45L8.34 12.26L8.21 12.1L8.05 11.97L7.86 11.87L7.66 11.81L7.45 11.79L5.13 11.79ZM17.79 11.67L17.66 11.29L17.49 10.92L17.28 10.58L17.04 10.26L16.77 9.96L16.47 9.69L16.15 9.45L15.8 9.24L15.44 9.07L15.06 8.93L14.67 8.84L14.27 8.78L13.87 8.76L13.47 8.78L13.07 8.84L12.68 8.93L12.3 9.07L11.94 9.24L11.59 9.45L11.27 9.69L10.97 9.96L10.7 10.26L10.46 10.58L10.25 10.92L10.08 11.29L9.95 11.67L9.85 12.06L9.79 12.46L9.77 12.86L9.79 13.26L9.85 13.66L9.95 14.05L10.08 14.43L10.25 14.79L10.46 15.14L10.7 15.46L10.97 15.76L11.27 16.03L11.59 16.27L11.94 16.47L12.3 16.65L12.68 16.78L13.07 16.88L13.47 16.94L13.87 16.96L14.27 16.94L14.67 16.88L15.06 16.78L15.44 16.65L15.8 16.47L16.15 16.27L16.47 16.03L16.77 15.76L17.04 15.46L17.28 15.14L17.49 14.79L17.66 14.43L17.79 14.05L17.89 13.66L17.95 13.26L17.97 12.86L17.95 12.46L17.89 12.06ZM22.92 13.88L23.2 13.75L23.43 13.54L23.59 13.27L23.67 12.96L23.67 12.75L23.63 12.55L23.49 12.26L23.28 12.03L23.01 11.87L22.81 11.81L22.61 11.79L20.29 11.79L20.08 11.81L19.88 11.87L19.61 12.03L19.4 12.26L19.3 12.45L19.24 12.65L19.22 12.86L19.24 13.07L19.3 13.27L19.4 13.45L19.53 13.61L19.69 13.75L19.88 13.85L20.08 13.91L20.29 13.93L22.61 13.93ZM6.75 18.53L6.64 18.83L6.63 19.14L6.7 19.44L6.87 19.71L7.1 19.92L7.38 20.06L7.59 20.1L7.8 20.1L8.1 20.02L8.29 19.92L8.45 19.79L10.09 18.15L10.22 17.99L10.32 17.8L10.4 17.5L10.4 17.29L10.32 16.99L10.22 16.8L10.09 16.64L9.93 16.51L9.74 16.41L9.54 16.35L9.33 16.33L9.12 16.35L8.92 16.41L8.74 16.51L8.58 16.64L6.94 18.28ZM19.64 20.02L19.94 20.1L20.26 20.08L20.55 19.98L20.8 19.79L20.99 19.54L21.1 19.24L21.11 18.93L21.04 18.63L20.94 18.44L20.8 18.28L19.16 16.64L19 16.51L18.72 16.37L18.51 16.33L18.3 16.33L18 16.41L17.81 16.51L17.65 16.64L17.52 16.8L17.42 16.99L17.36 17.19L17.34 17.4L17.36 17.6L17.42 17.8L17.52 17.99L17.65 18.15L19.29 19.79L19.45 19.92ZM12.88 22L13.04 22.27L13.28 22.48L13.56 22.62L13.87 22.66L14.18 22.62L14.46 22.48L14.7 22.27L14.86 22L14.92 21.8L14.94 21.59L14.94 19.28L14.92 19.07L14.86 18.87L14.7 18.6L14.46 18.39L14.28 18.29L14.08 18.23L13.87 18.21L13.66 18.23L13.46 18.29L13.28 18.39L13.11 18.52L12.98 18.68L12.88 18.87L12.82 19.07L12.8 19.28L12.8 21.59L12.82 21.8Z"), "Screen" to ours("blend-screen", 26f, "M14.82 5.08L14.69 4.92L14.52 4.78L14.34 4.68L14.13 4.62L13.92 4.6L13.71 4.62L13.5 4.68L13.32 4.78L13.15 4.92L13.02 5.08L12.92 5.27L12.86 5.47L12.83 5.69L12.86 5.9L12.92 6.1L13.02 6.29L13.15 6.45L13.32 6.59L13.5 6.69L13.71 6.75L13.92 6.77L14.13 6.75L14.34 6.69L14.52 6.59L14.69 6.45L14.82 6.29L14.92 6.1L14.98 5.9L15.01 5.69L14.98 5.47L14.92 5.27ZM19.78 7.14L19.65 6.97L19.48 6.84L19.3 6.74L19.09 6.68L18.88 6.65L18.67 6.68L18.46 6.74L18.28 6.84L18.11 6.97L17.98 7.14L17.88 7.32L17.82 7.53L17.79 7.74L17.82 7.95L17.88 8.16L17.98 8.34L18.11 8.51L18.28 8.64L18.46 8.74L18.67 8.8L18.88 8.83L19.09 8.8L19.3 8.74L19.48 8.64L19.65 8.51L19.78 8.34L19.88 8.16L19.94 7.95L19.97 7.74L19.94 7.53L19.88 7.32ZM9.86 7.14L9.73 6.97L9.56 6.84L9.38 6.74L9.17 6.68L8.96 6.65L8.75 6.68L8.54 6.74L8.36 6.84L8.19 6.97L8.06 7.14L7.96 7.32L7.9 7.53L7.87 7.74L7.9 7.95L7.96 8.16L8.06 8.34L8.19 8.51L8.36 8.64L8.54 8.74L8.75 8.8L8.96 8.83L9.17 8.8L9.38 8.74L9.56 8.64L9.73 8.51L9.86 8.34L9.96 8.16L10.02 7.95L10.05 7.74L10.02 7.53L9.96 7.32ZM17.89 12.18L17.79 11.66L17.62 11.17L17.51 10.93L17.25 10.47L16.93 10.06L16.75 9.87L16.36 9.52L16.15 9.37L15.69 9.11L15.45 9L14.96 8.83L14.7 8.77L14.18 8.7L13.92 8.69L13.66 8.7L13.14 8.77L12.88 8.83L12.39 9L11.92 9.23L11.69 9.37L11.48 9.52L11.09 9.87L10.74 10.26L10.59 10.47L10.45 10.7L10.22 11.17L10.05 11.66L9.99 11.92L9.92 12.44L9.91 12.7L9.95 13.22L9.99 13.48L10.12 13.99L10.22 14.23L10.45 14.7L10.59 14.93L10.74 15.14L11.09 15.53L11.48 15.88L11.69 16.03L12.15 16.29L12.39 16.4L12.88 16.57L13.14 16.63L13.4 16.67L13.92 16.71L14.44 16.67L14.7 16.63L14.96 16.57L15.45 16.4L15.92 16.17L16.15 16.03L16.56 15.71L16.75 15.53L16.93 15.34L17.25 14.93L17.39 14.7L17.62 14.23L17.79 13.74L17.85 13.48L17.89 13.22L17.93 12.7ZM21.84 12.1L21.7 11.93L21.54 11.8L21.35 11.7L21.15 11.64L20.93 11.61L20.72 11.64L20.52 11.7L20.33 11.8L20.17 11.93L20.03 12.1L19.93 12.28L19.87 12.49L19.85 12.7L19.87 12.91L19.93 13.12L20.03 13.3L20.17 13.47L20.33 13.6L20.52 13.7L20.72 13.76L20.93 13.79L21.15 13.76L21.35 13.7L21.54 13.6L21.7 13.47L21.84 13.3L21.94 13.12L22 12.91L22.02 12.7L22 12.49L21.94 12.28ZM7.81 12.1L7.67 11.93L7.51 11.8L7.32 11.7L7.12 11.64L6.91 11.61L6.69 11.64L6.49 11.7L6.3 11.8L6.14 11.93L6 12.1L5.9 12.28L5.84 12.49L5.82 12.7L5.84 12.91L5.9 13.12L6 13.3L6.14 13.47L6.3 13.6L6.49 13.7L6.69 13.76L6.91 13.79L7.12 13.76L7.32 13.7L7.51 13.6L7.67 13.47L7.81 13.3L7.91 13.12L7.97 12.91L7.99 12.7L7.97 12.49L7.91 12.28ZM9.86 17.06L9.73 16.89L9.56 16.76L9.38 16.66L9.17 16.6L8.96 16.57L8.75 16.6L8.54 16.66L8.36 16.76L8.19 16.89L8.06 17.06L7.96 17.24L7.9 17.45L7.87 17.66L7.9 17.87L7.96 18.08L8.06 18.26L8.19 18.43L8.36 18.56L8.54 18.66L8.75 18.72L8.96 18.75L9.17 18.72L9.38 18.66L9.56 18.56L9.73 18.43L9.86 18.26L9.96 18.08L10.02 17.87L10.05 17.66L10.02 17.45L9.96 17.24ZM19.65 16.89L19.48 16.76L19.3 16.66L19.09 16.6L18.88 16.57L18.67 16.6L18.46 16.66L18.28 16.76L18.11 16.89L17.98 17.06L17.88 17.24L17.82 17.45L17.79 17.66L17.82 17.87L17.88 18.08L17.98 18.26L18.11 18.43L18.28 18.56L18.46 18.66L18.67 18.72L18.88 18.75L19.09 18.72L19.3 18.66L19.48 18.56L19.65 18.43L19.78 18.26L19.88 18.08L19.94 17.87L19.97 17.66L19.94 17.45L19.88 17.24L19.78 17.06ZM14.69 18.95L14.52 18.81L14.34 18.71L14.13 18.65L13.92 18.63L13.71 18.65L13.5 18.71L13.32 18.81L13.15 18.95L13.02 19.11L12.92 19.3L12.86 19.5L12.83 19.71L12.86 19.93L12.92 20.13L13.02 20.32L13.15 20.48L13.32 20.62L13.5 20.72L13.71 20.78L13.92 20.8L14.13 20.78L14.34 20.72L14.52 20.62L14.69 20.48L14.82 20.32L14.92 20.13L14.98 19.93L15.01 19.71L14.98 19.5L14.92 19.3L14.82 19.11Z"), "Color Dodge" to ours("blend-color-dodge", 26f, "M13.72 4.98L12.07 11.04L6.07 12.66L6 12.71L5.98 12.77L6.01 12.85L6.07 12.88L12.07 14.5L13.69 20.5L13.72 20.56L13.8 20.59L13.88 20.56L13.91 20.5L15.53 14.5L21.53 12.88L21.59 12.85L21.62 12.77L21.59 12.69L21.53 12.66L15.53 11.04L13.91 5.04L13.88 4.98L13.8 4.95Z"), diff --git a/assets/icons/procraft/blend-greater.svg b/assets/icons/procraft/blend-greater.svg new file mode 100644 index 00000000..7051ff7e --- /dev/null +++ b/assets/icons/procraft/blend-greater.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/slots.tsv b/assets/icons/slots.tsv index 6c51d6a3..432567b5 100644 --- a/assets/icons/slots.tsv +++ b/assets/icons/slots.tsv @@ -213,6 +213,7 @@ blend-color-burn 26 26 7.56 4.8 21.47 20.5 blend-linear-burn 26 26 9.7 4.85 19.04 19.74 blend-darker-color 26 26 5.77 4.85 21.77 20.85 blend-normal 26 26 5.72 4.7 21.92 20.9 +blend-greater 26 26 5.72 4.7 21.92 20.9 blend-lighten 26 26 4.07 2.81 23.67 22.91 blend-screen 26 26 5.82 4.6 22.02 20.8 blend-color-dodge 26 26 5.96 4.95 21.64 20.59 diff --git a/desktop/src/icons.rs b/desktop/src/icons.rs index 8ccd7a73..cfad9468 100644 --- a/desktop/src/icons.rs +++ b/desktop/src/icons.rs @@ -144,6 +144,9 @@ pub const BLEND_GLYPHS: &[(&str, Icon)] = &[ ] }), ("Normal", Icon { name: "blend-normal", width: 26.0, height: 26.0, parts: &[ Part { d: "M21.92 12.8L21.88 12.01L21.76 11.22L21.57 10.45L21.3 9.7L20.96 8.98L20.55 8.3L20.08 7.66L19.55 7.07L18.96 6.54L18.32 6.07L17.64 5.66L16.92 5.32L16.17 5.05L15.4 4.86L14.61 4.74L13.82 4.7L13.03 4.74L12.24 4.86L11.47 5.05L10.72 5.32L10 5.66L9.32 6.07L8.68 6.54L8.09 7.07L7.56 7.66L7.09 8.3L6.68 8.98L6.34 9.7L6.07 10.45L5.88 11.22L5.76 12.01L5.72 12.8L5.76 13.59L5.88 14.38L6.07 15.15L6.34 15.9L6.68 16.62L7.09 17.3L7.56 17.94L8.09 18.53L8.68 19.06L9.32 19.53L10 19.94L10.72 20.28L11.47 20.55L12.24 20.74L13.03 20.86L13.82 20.9L14.61 20.86L15.4 20.74L16.17 20.55L16.92 20.28L17.64 19.94L18.32 19.53L18.96 19.06L19.55 18.53L20.08 17.94L20.55 17.3L20.96 16.62L21.3 15.9L21.57 15.15L21.76 14.38L21.88 13.59Z", stroke: 0.0, fill: true, even_odd: true }, +] }), + ("Greater", Icon { name: "blend-greater", width: 26.0, height: 26.0, parts: &[ + Part { d: "M12.1 7.02L11.53 6.99L10.96 7.02L10.4 7.1L9.84 7.24L9.31 7.43L8.79 7.68L8.3 7.97L7.84 8.31L7.42 8.69L7.04 9.11L6.7 9.57L6.41 10.06L6.16 10.58L5.97 11.11L5.83 11.67L5.75 12.23L5.72 12.8L5.75 13.37L5.83 13.93L5.97 14.49L6.16 15.02L6.41 15.54L6.7 16.03L7.04 16.49L7.42 16.91L7.84 17.29L8.3 17.63L8.79 17.92L9.31 18.17L9.84 18.36L10.4 18.5L10.96 18.58L11.53 18.61L12.1 18.58L12.66 18.5L13.12 18.39L12.6 18.08L12.24 17.82L11.92 17.55L11.38 17.02L10.71 16.94L10.3 16.84L9.91 16.7L9.54 16.53L9.18 16.31L8.85 16.07L8.54 15.79L8.26 15.48L8.02 15.15L7.8 14.79L7.63 14.42L7.49 14.03L7.39 13.62L7.33 13.21L7.3 12.8L7.33 12.39L7.39 11.98L7.49 11.57L7.63 11.18L7.8 10.81L8.02 10.45L8.26 10.12L8.54 9.81L8.85 9.53L9.18 9.29L9.54 9.07L9.91 8.9L10.3 8.76L10.71 8.66L11.38 8.58L11.92 8.05L12.24 7.78L12.6 7.52L13.12 7.21L12.66 7.1ZM21.92 12.8L21.87 12.04L21.72 11.3L21.61 10.93L21.32 10.23L21.14 9.89L20.72 9.26L20.22 8.69L19.94 8.43L19.34 7.97L19.01 7.77L18.68 7.59L17.98 7.3L17.61 7.19L16.87 7.04L16.11 6.99L15.73 7L15.35 7.04L14.61 7.19L13.89 7.43L13.54 7.59L12.88 7.97L12.57 8.19L12 8.69L11.74 8.97L11.5 9.26L11.08 9.89L10.74 10.58L10.61 10.93L10.41 11.67L10.35 12.04L10.3 12.8L10.31 13.18L10.35 13.56L10.5 14.3L10.74 15.02L10.9 15.37L11.28 16.03L11.5 16.34L12 16.91L12.28 17.17L12.88 17.63L13.2 17.83L13.54 18.01L14.24 18.3L14.61 18.41L15.35 18.56L15.73 18.6L16.49 18.6L17.24 18.5L17.61 18.41L17.98 18.3L18.68 18.01L19.01 17.83L19.65 17.41L19.94 17.17L20.48 16.63L20.94 16.03L21.14 15.71L21.48 15.02L21.61 14.67L21.72 14.3L21.87 13.56Z", stroke: 0.0, fill: true, even_odd: true }, ] }), ("Lighten", Icon { name: "blend-lighten", width: 26.0, height: 26.0, parts: &[ Part { d: "M14.89 3.81L14.76 3.53L14.55 3.3L14.28 3.13L13.97 3.06L13.66 3.07L13.37 3.18L13.11 3.37L12.98 3.53L12.88 3.71L12.82 3.91L12.8 4.12L12.8 6.44L12.82 6.65L12.88 6.85L13.04 7.12L13.19 7.27L13.46 7.43L13.66 7.49L13.87 7.51L14.08 7.49L14.28 7.43L14.46 7.33L14.63 7.2L14.76 7.03L14.86 6.85L14.92 6.65L14.94 6.44L14.94 4.12ZM20.99 7.18L21.1 6.89L21.11 6.58L21.04 6.27L20.94 6.09L20.8 5.92L20.55 5.74L20.26 5.63L19.94 5.62L19.64 5.69L19.45 5.79L19.29 5.92L17.65 7.56L17.52 7.73L17.42 7.91L17.34 8.21L17.34 8.42L17.42 8.73L17.52 8.91L17.65 9.08L17.81 9.21L18 9.31L18.2 9.37L18.41 9.39L18.62 9.37L18.82 9.31L19 9.21L19.16 9.08L20.8 7.44ZM8.2 5.74L7.9 5.63L7.59 5.62L7.28 5.69L7.01 5.85L6.87 6L6.75 6.18L6.64 6.47L6.63 6.79L6.7 7.09L6.8 7.27L6.94 7.44L8.58 9.08L8.74 9.21L8.92 9.31L9.23 9.38L9.44 9.38L9.74 9.31L9.93 9.21L10.09 9.08L10.22 8.91L10.32 8.73L10.38 8.53L10.4 8.32L10.38 8.11L10.32 7.91L10.22 7.73L10.09 7.56L8.45 5.92ZM4.82 11.83L4.54 11.97L4.31 12.18L4.15 12.45L4.07 12.75L4.09 13.07L4.19 13.36L4.38 13.61L4.54 13.75L4.73 13.85L4.93 13.91L5.13 13.93L7.45 13.93L7.66 13.91L7.86 13.85L8.13 13.68L8.34 13.45L8.44 13.27L8.5 13.07L8.52 12.86L8.5 12.65L8.44 12.45L8.34 12.26L8.21 12.1L8.05 11.97L7.86 11.87L7.66 11.81L7.45 11.79L5.13 11.79ZM17.79 11.67L17.66 11.29L17.49 10.92L17.28 10.58L17.04 10.26L16.77 9.96L16.47 9.69L16.15 9.45L15.8 9.24L15.44 9.07L15.06 8.93L14.67 8.84L14.27 8.78L13.87 8.76L13.47 8.78L13.07 8.84L12.68 8.93L12.3 9.07L11.94 9.24L11.59 9.45L11.27 9.69L10.97 9.96L10.7 10.26L10.46 10.58L10.25 10.92L10.08 11.29L9.95 11.67L9.85 12.06L9.79 12.46L9.77 12.86L9.79 13.26L9.85 13.66L9.95 14.05L10.08 14.43L10.25 14.79L10.46 15.14L10.7 15.46L10.97 15.76L11.27 16.03L11.59 16.27L11.94 16.47L12.3 16.65L12.68 16.78L13.07 16.88L13.47 16.94L13.87 16.96L14.27 16.94L14.67 16.88L15.06 16.78L15.44 16.65L15.8 16.47L16.15 16.27L16.47 16.03L16.77 15.76L17.04 15.46L17.28 15.14L17.49 14.79L17.66 14.43L17.79 14.05L17.89 13.66L17.95 13.26L17.97 12.86L17.95 12.46L17.89 12.06ZM22.92 13.88L23.2 13.75L23.43 13.54L23.59 13.27L23.67 12.96L23.67 12.75L23.63 12.55L23.49 12.26L23.28 12.03L23.01 11.87L22.81 11.81L22.61 11.79L20.29 11.79L20.08 11.81L19.88 11.87L19.61 12.03L19.4 12.26L19.3 12.45L19.24 12.65L19.22 12.86L19.24 13.07L19.3 13.27L19.4 13.45L19.53 13.61L19.69 13.75L19.88 13.85L20.08 13.91L20.29 13.93L22.61 13.93ZM6.75 18.53L6.64 18.83L6.63 19.14L6.7 19.44L6.87 19.71L7.1 19.92L7.38 20.06L7.59 20.1L7.8 20.1L8.1 20.02L8.29 19.92L8.45 19.79L10.09 18.15L10.22 17.99L10.32 17.8L10.4 17.5L10.4 17.29L10.32 16.99L10.22 16.8L10.09 16.64L9.93 16.51L9.74 16.41L9.54 16.35L9.33 16.33L9.12 16.35L8.92 16.41L8.74 16.51L8.58 16.64L6.94 18.28ZM19.64 20.02L19.94 20.1L20.26 20.08L20.55 19.98L20.8 19.79L20.99 19.54L21.1 19.24L21.11 18.93L21.04 18.63L20.94 18.44L20.8 18.28L19.16 16.64L19 16.51L18.72 16.37L18.51 16.33L18.3 16.33L18 16.41L17.81 16.51L17.65 16.64L17.52 16.8L17.42 16.99L17.36 17.19L17.34 17.4L17.36 17.6L17.42 17.8L17.52 17.99L17.65 18.15L19.29 19.79L19.45 19.92ZM12.88 22L13.04 22.27L13.28 22.48L13.56 22.62L13.87 22.66L14.18 22.62L14.46 22.48L14.7 22.27L14.86 22L14.92 21.8L14.94 21.59L14.94 19.28L14.92 19.07L14.86 18.87L14.7 18.6L14.46 18.39L14.28 18.29L14.08 18.23L13.87 18.21L13.66 18.23L13.46 18.29L13.28 18.39L13.11 18.52L12.98 18.68L12.88 18.87L12.82 19.07L12.8 19.28L12.8 21.59L12.82 21.8Z", stroke: 0.0, fill: true, even_odd: true }, diff --git a/docs/icons/STYLE_GUIDE.md b/docs/icons/STYLE_GUIDE.md index ebdc3fd7..501ad681 100644 --- a/docs/icons/STYLE_GUIDE.md +++ b/docs/icons/STYLE_GUIDE.md @@ -255,4 +255,5 @@ Screen a disc ringed with dots; Color Dodge a sparkle; Add a plus in a ring; Sof Hard Light a spotlight, Vivid Light a bolt, Linear Light a torch, Pin Light a thumbtack; Hard Mix four squares; Subtract a minus, Divide a division sign; Hue the colour wheel, Saturation a drop filled halfway, Color three overlapping circles, Luminosity a disc half solid on the slant; Behind -a solid sheet behind an outline; Height an arrow standing on a base, Linear Height a ramp. +a solid sheet behind an outline; Height an arrow standing on a base, Linear Height a ramp. Greater +a solid disc in front of a ring. diff --git a/tools/icons/draw_icons.py b/tools/icons/draw_icons.py index 80817848..77e3d358 100644 --- a/tools/icons/draw_icons.py +++ b/tools/icons/draw_icons.py @@ -1550,6 +1550,13 @@ def half_disc(): return unary_union([solid, ring(12, 12, 8.1, 1.8)]) +def greater(): + """Greater: the more opaque of two discs — the solid one covers the outline.""" + front = Point(14.6, 12.0).buffer(DISC_R, quad_segs=24) + back = ring(9.4, 12.0, DISC_R - 0.9, 1.8).difference(front.buffer(GAP / 2)) + return unary_union([back, front]) + + def behind(): """Behind: a solid sheet behind a sheet's outline.""" front = soft(box(3.0, 9.0, 15.0, 21.0), 1.4) @@ -1569,7 +1576,7 @@ def ramp(): BLENDS = {'multiply': overlap_dark, 'darken': darken, 'shade': sunset, 'color-burn': flame, 'linear-burn': campfire, - 'darker-color': darker_colour, 'normal': lambda: Point(12, 12).buffer(8.0, quad_segs=32), + 'darker-color': darker_colour, 'normal': lambda: Point(12, 12).buffer(8.0, quad_segs=32), 'greater': greater, 'lighten': sun_object, 'screen': dim_sun, 'color-dodge': lambda: star(12, 12, 10.0, 0.3), 'add': plus_in_ring, 'lighter-color': lighter_colour, 'overlay': overlap_rings, 'soft-light': lamp, 'hard-light': spotlight, 'vivid-light': flash, 'linear-light': torch, 'pin-light': thumbtack, 'hard-mix': four_squares, From 9f4263ccfa0197fd56e01f7efe81892fc5406896 Mon Sep 17 00:00:00 2001 From: Jasyd Caballero Date: Mon, 2 Nov 2026 22:41:30 -0500 Subject: [PATCH 3/4] improve logic for the blend mode --- crates/gpu/src/shaders/blend.wgsl | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/crates/gpu/src/shaders/blend.wgsl b/crates/gpu/src/shaders/blend.wgsl index 736bcf1b..11f52ec0 100644 --- a/crates/gpu/src/shaders/blend.wgsl +++ b/crates/gpu/src/shaders/blend.wgsl @@ -221,7 +221,7 @@ fn blend(b: vec4, s: vec4, mode: u32) -> vec4 { return b + s * (1.0 - b.a); } if (mode == 30u) { - return select(b, s, s.a >= b.a); + return blend_greater(b, s); } let cb = unpremultiply(b); let cs = unpremultiply(s); @@ -229,3 +229,20 @@ fn blend(b: vec4, s: vec4, mode: u32) -> vec4 { let rgb = s.a * (1.0 - b.a) * cs + s.a * b.a * mixed + (1.0 - s.a) * b.rgb; return vec4(rgb, s.a + b.a * (1.0 - s.a)); } + +fn blend_greater(b: vec4, s: vec4) -> vec4 { + let dA = b.a; + let sa = s.a; + if (dA >= 1.0 || sa <= 0.0) { + return b; + } + let w = 1.0 / (1.0 + exp(-40.0 * (dA - sa))); + var a = dA * w + sa * (1.0 - w); + a = clamp(a, 0.0, 1.0); + a = max(a, dA); // the backdrop's opacity never falls + // the source-over opacity that would have raised dA to a + let fake = 1.0 - (1.0 - a) / max(1.0 - dA, 1e-6); + // b.rgb is already premultiplied; the source enters at `fake`, not at its own alpha + let cs = unpremultiply(s); + return vec4(mix(b.rgb, cs, fake), a); +} From fef56a73a3f4788b9431b656b304bb9ae06c1782 Mon Sep 17 00:00:00 2001 From: Jasyd Caballero Date: Tue, 3 Nov 2026 22:45:23 -0500 Subject: [PATCH 4/4] Replace Krita GPL logic in blend_greater with clean implementation --- crates/gpu/src/shaders/blend.wgsl | 29 ++++++++++++++++++--------- crates/gpu/src/shaders/composite.wgsl | 1 - 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/crates/gpu/src/shaders/blend.wgsl b/crates/gpu/src/shaders/blend.wgsl index 11f52ec0..c76ffce5 100644 --- a/crates/gpu/src/shaders/blend.wgsl +++ b/crates/gpu/src/shaders/blend.wgsl @@ -230,19 +230,30 @@ fn blend(b: vec4, s: vec4, mode: u32) -> vec4 { return vec4(rgb, s.a + b.a * (1.0 - s.a)); } +// Standard Greater/Max Alpha Blend for premultiplied targets fn blend_greater(b: vec4, s: vec4) -> vec4 { let dA = b.a; let sa = s.a; + + // Early exit if background is fully opaque or source is completely transparent if (dA >= 1.0 || sa <= 0.0) { return b; } - let w = 1.0 / (1.0 + exp(-40.0 * (dA - sa))); - var a = dA * w + sa * (1.0 - w); - a = clamp(a, 0.0, 1.0); - a = max(a, dA); // the backdrop's opacity never falls - // the source-over opacity that would have raised dA to a - let fake = 1.0 - (1.0 - a) / max(1.0 - dA, 1e-6); - // b.rgb is already premultiplied; the source enters at `fake`, not at its own alpha - let cs = unpremultiply(s); - return vec4(mix(b.rgb, cs, fake), a); + + // Target alpha is determined by the greater of the two layers + let target_a = max(dA, sa); + + // Unpremultiply both inputs to operate in straight (unassociated) color space + let color_b = select(vec3(0.0), b.rgb / dA, dA > 0.0); + let color_s = select(vec3(0.0), s.rgb / sa, sa > 0.0); + + // Calculate normalized weight based on relative alpha contribution + let total_a = dA + sa; + let weight = select(0.5, sa / total_a, total_a > 0.0); + + // Blend in straight RGB space + let blended_rgb = mix(color_b, color_s, weight); + + // Re-premultiply by the target alpha for output consistency + return vec4(blended_rgb * target_a, target_a); } diff --git a/crates/gpu/src/shaders/composite.wgsl b/crates/gpu/src/shaders/composite.wgsl index 4a9ff085..de70310b 100644 --- a/crates/gpu/src/shaders/composite.wgsl +++ b/crates/gpu/src/shaders/composite.wgsl @@ -284,4 +284,3 @@ fn stroke_into(b: vec4, s: vec4) -> vec4 { let full = vec4((1.0 - b.a) * cs + b.a * mixed, 1.0); return unsquared(mix(squared(b), squared(full), s.a)); } -