From c6d7df30997d141ae5ce498da8d3a41fbb07ab9d Mon Sep 17 00:00:00 2001 From: fractioneater Date: Sun, 28 Dec 2025 08:52:28 -0500 Subject: [PATCH 01/10] Git is weird; let's try that again --- src/effects/effects.js | 51 ++++++++++++++ src/effects/mix.js | 147 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100644 src/effects/mix.js diff --git a/src/effects/effects.js b/src/effects/effects.js index 931d3455..bcaaa3bc 100644 --- a/src/effects/effects.js +++ b/src/effects/effects.js @@ -3,6 +3,7 @@ import { NativeStaticBlurEffect } from '../effects/native_static_gaussian_blur.j import { GaussianBlurEffect } from '../effects/gaussian_blur.js'; import { MonteCarloBlurEffect } from '../effects/monte_carlo_blur.js'; import { ColorEffect } from '../effects/color.js'; +import { MixEffect } from '../effects/mix.js'; import { NoiseEffect } from '../effects/noise.js'; import { CornerEffect } from '../effects/corner.js'; import { DownscaleEffect } from './downscale.js'; @@ -33,6 +34,7 @@ export function get_effects_groups(_ = _ => "") { "derivative", "noise", "color", + "mix", "rgb_to_hsl", "hsl_to_rgb" ] @@ -172,6 +174,55 @@ export function get_supported_effects(_ = () => "") { } }, + mix: { + class: MixEffect, + name: _("Mix"), + description: _("An effect that blends a solid color or image into the pipeline."), + is_advanced: true, + editable_params: { + color: { + name: _("Color"), + description: _("The color to blend in."), + type: "rgb" + }, + opacity: { + name: _("Opacity"), + description: _("The strength of the effect"), + type: "float", + min: 0., + max: 1., + increment: 0.01, + big_increment: 0.1, + digits: 2 + }, + blend_mode: { + name: _("Blend mode"), + description: _("The mode used to blend the image or color."), + type: "dropdown", + options: [ + _("Normal"), + _("Multiply"), + _("Screen"), + _("Overlay"), + _("Darken"), + _("Lighten"), + _("Color dodge"), + _("Color burn"), + _("Hard light"), + _("Soft light"), + _("Difference"), + _("Exclusion"), + _("Hue"), + _("Saturation"), + _("Color"), + _("Luminosity"), + _("Plus darker"), + _("Plus lighter") + ] + } + } + }, + pixelize: { class: PixelizeEffect, name: _("Pixelize"), diff --git a/src/effects/mix.js b/src/effects/mix.js new file mode 100644 index 00000000..e84cb472 --- /dev/null +++ b/src/effects/mix.js @@ -0,0 +1,147 @@ +import GObject from 'gi://GObject'; + +import * as utils from '../conveniences/utils.js'; +const Shell = await utils.import_in_shell_only('gi://Shell'); +const Clutter = await utils.import_in_shell_only('gi://Clutter'); + +const SHADER_FILENAME = 'color.glsl'; +const DEFAULT_PARAMS = { + color: [0.0, 0.0, 0.0] +}; + + +export const MixEffect = utils.IS_IN_PREFERENCES ? + { default_params: DEFAULT_PARAMS } : + new GObject.registerClass({ + GTypeName: "MixEffect", + Properties: { + 'red': GObject.ParamSpec.double( + `red`, + `Red`, + `Red value in shader`, + GObject.ParamFlags.READWRITE, + 0.0, 1.0, + 0.0, + ), + 'green': GObject.ParamSpec.double( + `green`, + `Green`, + `Green value in shader`, + GObject.ParamFlags.READWRITE, + 0.0, 1.0, + 0.0, + ), + 'blue': GObject.ParamSpec.double( + `blue`, + `Blue`, + `Blue value in shader`, + GObject.ParamFlags.READWRITE, + 0.0, 1.0, + 0.0, + ), + 'opacity': GObject.ParamSpec.double( + `opacity`, + `Opacity`, + `Opacity`, + GObject.ParamFlags.READWRITE, + 0.0, 1.0, + 0.0, + ), + 'blend_mode': GObject.ParamSpec.int( + `blend_mode`, + `Blend mode`, + `Blend mode`, + GObject.ParamFlags.READWRITE, + 0, 17, + 0, + ), + } + // Normal (0), Multiply (1), Screen (2), Overlay (3), Darken (4), Lighten (5), Color dodge (6), Color burn (7), Hard light (8), + // Soft light (9), Difference (10), Exclusion (11), Hue (12), Saturation (13), Color (14), Luminosity (15), Plus darker (16), + // Plus lighter (17) + }, class MixEffect extends Clutter.ShaderEffect { + constructor(params) { + super(params); + + this._red = null; + this._green = null; + this._blue = null; + this._opacity = null; + + // set shader source + this._source = utils.get_shader_source(Shell, SHADER_FILENAME, import.meta.url); + if (this._source) + this.set_shader_source(this._source); + + utils.setup_params(this, params); + } + + static get default_params() { + return DEFAULT_PARAMS; + } + + get red() { + return this._red; + } + + set red(value) { + if (this._red !== value) { + this._red = value; + + this.set_uniform_value('red', parseFloat(this._red - 1e-6)); + } + } + + get green() { + return this._green; + } + + set green(value) { + if (this._green !== value) { + this._green = value; + + this.set_uniform_value('green', parseFloat(this._green - 1e-6)); + } + } + + get blue() { + return this._blue; + } + + set blue(value) { + if (this._blue !== value) { + this._blue = value; + + this.set_uniform_value('blue', parseFloat(this._blue - 1e-6)); + } + } + + get opacity() { + return this._opacity; + } + + set opacity(value) { + if (this._opacity !== value) { + this._opacity = value; + + this.set_uniform_value('blend', parseFloat(this._opacity - 1e-6)); + this.set_enabled(this.opacity > 0); + } + } + + set color(rgb) { + let [r, g, b] = rgb; + this.red = r; + this.green = g; + this.blue = b; + } + + get color() { + return [this.red, this.green, this.blue]; + } + + /// False set function, only cares about the color. Too hard to change. + set(params) { + this.color = params.color; + } + }); \ No newline at end of file From d5fa09569bfaeca133fbb87f0d72917100c895fa Mon Sep 17 00:00:00 2001 From: fractioneater Date: Sun, 28 Dec 2025 09:12:58 -0500 Subject: [PATCH 02/10] I can do this inside color, and make another effect for image --- src/effects/color.js | 23 ++++--- src/effects/effects.js | 28 +------- src/effects/mix.js | 147 ----------------------------------------- 3 files changed, 15 insertions(+), 183 deletions(-) delete mode 100644 src/effects/mix.js diff --git a/src/effects/color.js b/src/effects/color.js index 7de753d6..3555f337 100644 --- a/src/effects/color.js +++ b/src/effects/color.js @@ -1,6 +1,7 @@ import GObject from 'gi://GObject'; import * as utils from '../conveniences/utils.js'; + const Shell = await utils.import_in_shell_only('gi://Shell'); const Clutter = await utils.import_in_shell_only('gi://Clutter'); @@ -47,25 +48,29 @@ export const ColorEffect = utils.IS_IN_PREFERENCES ? 0.0, 1.0, 0.0, ), + 'blend_mode': GObject.ParamSpec.int( + `blend_mode`, + `Blend mode`, + `Blend mode`, + GObject.ParamFlags.READWRITE, + 0, 17, + 0, + ) } + // Normal (0), Multiply (1), Screen (2), Overlay (3), Darken (4), Lighten (5), Color dodge (6), Color burn (7), Hard light (8), + // Soft light (9), Difference (10), Exclusion (11), Hue (12), Saturation (13), Color (14), Luminosity (15), Plus darker (16), + // Plus lighter (17) }, class ColorEffect extends Clutter.ShaderEffect { constructor(params) { // initialize without color as a parameter - const { color, ...parent_params } = params; - super(parent_params); + super(params); - this._red = null; - this._green = null; - this._blue = null; - this._blend = null; + utils.setup_params(this, params); // set shader source this._source = utils.get_shader_source(Shell, SHADER_FILENAME, import.meta.url); if (this._source) this.set_shader_source(this._source); - - // set shader color - this.color = 'color' in params ? color : this.constructor.default_params.color; } static get default_params() { diff --git a/src/effects/effects.js b/src/effects/effects.js index bcaaa3bc..4ae8f4b4 100644 --- a/src/effects/effects.js +++ b/src/effects/effects.js @@ -3,7 +3,6 @@ import { NativeStaticBlurEffect } from '../effects/native_static_gaussian_blur.j import { GaussianBlurEffect } from '../effects/gaussian_blur.js'; import { MonteCarloBlurEffect } from '../effects/monte_carlo_blur.js'; import { ColorEffect } from '../effects/color.js'; -import { MixEffect } from '../effects/mix.js'; import { NoiseEffect } from '../effects/noise.js'; import { CornerEffect } from '../effects/corner.js'; import { DownscaleEffect } from './downscale.js'; @@ -34,7 +33,6 @@ export function get_effects_groups(_ = _ => "") { "derivative", "noise", "color", - "mix", "rgb_to_hsl", "hsl_to_rgb" ] @@ -170,34 +168,10 @@ export function get_supported_effects(_ = () => "") { name: _("Color"), description: _("The color to blend in. The blending amount is controled by the opacity of the color."), type: "rgba" - } - } - }, - - mix: { - class: MixEffect, - name: _("Mix"), - description: _("An effect that blends a solid color or image into the pipeline."), - is_advanced: true, - editable_params: { - color: { - name: _("Color"), - description: _("The color to blend in."), - type: "rgb" - }, - opacity: { - name: _("Opacity"), - description: _("The strength of the effect"), - type: "float", - min: 0., - max: 1., - increment: 0.01, - big_increment: 0.1, - digits: 2 }, blend_mode: { name: _("Blend mode"), - description: _("The mode used to blend the image or color."), + description: _("How the color is blended in."), type: "dropdown", options: [ _("Normal"), diff --git a/src/effects/mix.js b/src/effects/mix.js deleted file mode 100644 index e84cb472..00000000 --- a/src/effects/mix.js +++ /dev/null @@ -1,147 +0,0 @@ -import GObject from 'gi://GObject'; - -import * as utils from '../conveniences/utils.js'; -const Shell = await utils.import_in_shell_only('gi://Shell'); -const Clutter = await utils.import_in_shell_only('gi://Clutter'); - -const SHADER_FILENAME = 'color.glsl'; -const DEFAULT_PARAMS = { - color: [0.0, 0.0, 0.0] -}; - - -export const MixEffect = utils.IS_IN_PREFERENCES ? - { default_params: DEFAULT_PARAMS } : - new GObject.registerClass({ - GTypeName: "MixEffect", - Properties: { - 'red': GObject.ParamSpec.double( - `red`, - `Red`, - `Red value in shader`, - GObject.ParamFlags.READWRITE, - 0.0, 1.0, - 0.0, - ), - 'green': GObject.ParamSpec.double( - `green`, - `Green`, - `Green value in shader`, - GObject.ParamFlags.READWRITE, - 0.0, 1.0, - 0.0, - ), - 'blue': GObject.ParamSpec.double( - `blue`, - `Blue`, - `Blue value in shader`, - GObject.ParamFlags.READWRITE, - 0.0, 1.0, - 0.0, - ), - 'opacity': GObject.ParamSpec.double( - `opacity`, - `Opacity`, - `Opacity`, - GObject.ParamFlags.READWRITE, - 0.0, 1.0, - 0.0, - ), - 'blend_mode': GObject.ParamSpec.int( - `blend_mode`, - `Blend mode`, - `Blend mode`, - GObject.ParamFlags.READWRITE, - 0, 17, - 0, - ), - } - // Normal (0), Multiply (1), Screen (2), Overlay (3), Darken (4), Lighten (5), Color dodge (6), Color burn (7), Hard light (8), - // Soft light (9), Difference (10), Exclusion (11), Hue (12), Saturation (13), Color (14), Luminosity (15), Plus darker (16), - // Plus lighter (17) - }, class MixEffect extends Clutter.ShaderEffect { - constructor(params) { - super(params); - - this._red = null; - this._green = null; - this._blue = null; - this._opacity = null; - - // set shader source - this._source = utils.get_shader_source(Shell, SHADER_FILENAME, import.meta.url); - if (this._source) - this.set_shader_source(this._source); - - utils.setup_params(this, params); - } - - static get default_params() { - return DEFAULT_PARAMS; - } - - get red() { - return this._red; - } - - set red(value) { - if (this._red !== value) { - this._red = value; - - this.set_uniform_value('red', parseFloat(this._red - 1e-6)); - } - } - - get green() { - return this._green; - } - - set green(value) { - if (this._green !== value) { - this._green = value; - - this.set_uniform_value('green', parseFloat(this._green - 1e-6)); - } - } - - get blue() { - return this._blue; - } - - set blue(value) { - if (this._blue !== value) { - this._blue = value; - - this.set_uniform_value('blue', parseFloat(this._blue - 1e-6)); - } - } - - get opacity() { - return this._opacity; - } - - set opacity(value) { - if (this._opacity !== value) { - this._opacity = value; - - this.set_uniform_value('blend', parseFloat(this._opacity - 1e-6)); - this.set_enabled(this.opacity > 0); - } - } - - set color(rgb) { - let [r, g, b] = rgb; - this.red = r; - this.green = g; - this.blue = b; - } - - get color() { - return [this.red, this.green, this.blue]; - } - - /// False set function, only cares about the color. Too hard to change. - set(params) { - this.color = params.color; - } - }); \ No newline at end of file From 3007f24196e43d005df4d5fcff429830ba39d2b4 Mon Sep 17 00:00:00 2001 From: fractioneater Date: Sun, 28 Dec 2025 10:24:10 -0500 Subject: [PATCH 03/10] Shader blend modes --- src/effects/color.glsl | 100 ++++++++++++++++++++++++++++++++++++++++- src/effects/color.js | 12 +++++ 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/src/effects/color.glsl b/src/effects/color.glsl index 142b89bf..77be53b8 100644 --- a/src/effects/color.glsl +++ b/src/effects/color.glsl @@ -3,11 +3,109 @@ uniform float red; uniform float green; uniform float blue; uniform float blend; +uniform int mode; + +const int NORMAL = 0; +const int MULTIPLY = 1; +const int SCREEN = 2; +const int OVERLAY = 3; +const int DARKEN = 4; +const int LIGHTEN = 5; +const int COLOR_DODGE = 6; +const int COLOR_BURN = 7; +const int HARD_LIGHT = 8; +const int SOFT_LIGHT = 9; +const int DIFFERENCE = 10; +const int EXCLUSION = 11; +const int HUE = 12; +const int SATURATION = 13; +const int COLOR = 14; +const int LUMINOSITY = 15; +const int PLUS_DARKER = 16; +const int PLUS_LIGHTER = 17; + +vec3 rgb_to_hsl(vec3 c) { + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +vec3 hsl_to_rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +float soft_light_channel(float base, float blend) { + if (blend < 0.5) { + return base - (1.0 - 2.0 * blend) * base * (1.0 - base); + } else { + float d = (base < 0.25) + ? ((16.0 * base - 12.0) * base + 4.0) * base + : sqrt(base); + return base + (2.0 * blend - 1.0) * (d - base); + } +} + +vec3 blend(vec3 base, vec3 blend) { + if (mode == MULTIPLY) return base * blend; + if (mode == SCREEN) return 1 - (1 - base) * (1 - blend); + if (mode == OVERLAY) { + vec3 result; + result.r = base.r < 0.5 ? (2.0 * base.r * blend.r) : (1.0 - 2.0 * (1.0 - base.r) * (1.0 - blend.r)); + result.g = base.g < 0.5 ? (2.0 * base.g * blend.g) : (1.0 - 2.0 * (1.0 - base.g) * (1.0 - blend.g)); + result.b = base.b < 0.5 ? (2.0 * base.b * blend.b) : (1.0 - 2.0 * (1.0 - base.b) * (1.0 - blend.b)); + return result; + } + if (mode == DARKEN) return min(base, blend); + if (mode == LIGHTEN) return max(base, blend); + if (mode == COLOR_DODGE) return base / (1 - blend); + if (mode == COLOR_BURN) return 1 - (1 - base) / blend; + if (mode == HARD_LIGHT) { + vec3 result; + result.r = blend.r < 0.5 ? (2.0 * base.r * blend.r) : (1.0 - 2.0 * (1.0 - base.r) * (1.0 - blend.r)); + result.g = blend.g < 0.5 ? (2.0 * base.g * blend.g) : (1.0 - 2.0 * (1.0 - base.g) * (1.0 - blend.g)); + result.b = blend.b < 0.5 ? (2.0 * base.b * blend.b) : (1.0 - 2.0 * (1.0 - base.b) * (1.0 - blend.b)); + return result; + } + if (mode == SOFT_LIGHT) { + return vec3(soft_light_channel(base.r, blend.r), soft_light_channel(base.g, blend.g), soft_light_channel(base.b, blend.b)); + } + if (mode == DIFFERENCE) return abs(base - blend); + if (mode == EXCLUSION) return 0.5 - 2 * (base - 0.5) * (blend - 0.5); + if (mode == HUE) { + vec3 base_hsl = rgb_to_hsl(base); + vec3 base_hsl = rgb_to_hsl(blend); + return hsl_to_rgb(vec3(base_hsl.x, base_hsl.y, base_hsl.z)); + } + if (mode == SATURATION) { + vec3 base_hsl = rgb_to_hsl(base); + vec3 base_hsl = rgb_to_hsl(blend); + return hsl_to_rgb(vec3(base_hsl.x, base_hsl.y, base_hsl.z)); + } + if (mode == COLOR) { + vec3 base_hsl = rgb_to_hsl(base); + vec3 base_hsl = rgb_to_hsl(blend); + return hsl_to_rgb(vec3(base_hsl.x, base_hsl.y, base_hsl.z)); + } + if (mode == LUMINOSITY) { + vec3 base_hsl = rgb_to_hsl(base); + vec3 base_hsl = rgb_to_hsl(blend); + return hsl_to_rgb(vec3(base_hsl.x, base_hsl.y, base_hsl.z)); + } + if (mode == PLUS_DARKER) return base + blend - 1; + if (mode == PLUS_LIGHTER) return base + blend; + return blend; // For NORMAL +} void main() { vec4 c = texture2D(tex, cogl_tex_coord_in[0].st); vec3 pix_color = c.xyz; - vec3 color = vec3(red, green, blue); + vec3 color = blend(pix_color, vec3(red, green, blue)); cogl_color_out = vec4(mix(pix_color, color, blend), 1.); } \ No newline at end of file diff --git a/src/effects/color.js b/src/effects/color.js index 3555f337..7b447d8e 100644 --- a/src/effects/color.js +++ b/src/effects/color.js @@ -126,6 +126,18 @@ export const ColorEffect = utils.IS_IN_PREFERENCES ? } } + get blend_mode() { + return this._blend_mode; + } + + set blend_mode(value) { + if (this._blend_mode !== value) { + this._blend_mode = value; + + this.set_uniform_value('mode', this._blend_mode); + } + } + set color(rgba) { let [r, g, b, a] = rgba; this.red = r; From c5dce213e4eb91d70c27cc5192cf7a365f5eded7 Mon Sep 17 00:00:00 2001 From: fractioneater Date: Sun, 28 Dec 2025 10:30:47 -0500 Subject: [PATCH 04/10] Fix params --- src/effects/color.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/effects/color.js b/src/effects/color.js index 7b447d8e..5af6b8b7 100644 --- a/src/effects/color.js +++ b/src/effects/color.js @@ -7,7 +7,8 @@ const Clutter = await utils.import_in_shell_only('gi://Clutter'); const SHADER_FILENAME = 'color.glsl'; const DEFAULT_PARAMS = { - color: [0.0, 0.0, 0.0, 0.0] + color: [0.0, 0.0, 0.0, 0.0], + blend_mode: 0 }; @@ -63,14 +64,25 @@ export const ColorEffect = utils.IS_IN_PREFERENCES ? }, class ColorEffect extends Clutter.ShaderEffect { constructor(params) { // initialize without color as a parameter - super(params); + const { color, ...parent_params } = params; + super(parent_params); - utils.setup_params(this, params); + this._red = null; + this._green = null; + this._blue = null; + this._blend = null; + this._blend_mode = null; + + console.log(`(${this._red}, ${this._green}, ${this._blue}, ${this._blend}) with mode ${this._blend_mode}`); //- TODO: REMOVE // set shader source this._source = utils.get_shader_source(Shell, SHADER_FILENAME, import.meta.url); if (this._source) this.set_shader_source(this._source); + + // set params; utils.setup_params doesn't work here with color + this.color = 'color' in params ? color : this.constructor.default_params.color; + this.blend_mode = 'blend_mode' in params ? params.blend_mode : this.constructor.default_params.blend_mode; } static get default_params() { From 286723a3d486aa82fff82f0947ee291b767fce32 Mon Sep 17 00:00:00 2001 From: fractioneater Date: Sun, 28 Dec 2025 10:35:20 -0500 Subject: [PATCH 05/10] Fix shader name shadowing --- src/effects/color.glsl | 56 +++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/effects/color.glsl b/src/effects/color.glsl index 77be53b8..b85d79a5 100644 --- a/src/effects/color.glsl +++ b/src/effects/color.glsl @@ -40,72 +40,72 @@ vec3 hsl_to_rgb(vec3 c) { return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } -float soft_light_channel(float base, float blend) { - if (blend < 0.5) { - return base - (1.0 - 2.0 * blend) * base * (1.0 - base); +float soft_light_channel(float base, float _blend) { + if (_blend < 0.5) { + return base - (1.0 - 2.0 * _blend) * base * (1.0 - base); } else { float d = (base < 0.25) ? ((16.0 * base - 12.0) * base + 4.0) * base : sqrt(base); - return base + (2.0 * blend - 1.0) * (d - base); + return base + (2.0 * _blend - 1.0) * (d - base); } } -vec3 blend(vec3 base, vec3 blend) { - if (mode == MULTIPLY) return base * blend; - if (mode == SCREEN) return 1 - (1 - base) * (1 - blend); +vec3 get_blend(vec3 base, vec3 _blend) { + if (mode == MULTIPLY) return base * _blend; + if (mode == SCREEN) return 1 - (1 - base) * (1 - _blend); if (mode == OVERLAY) { vec3 result; - result.r = base.r < 0.5 ? (2.0 * base.r * blend.r) : (1.0 - 2.0 * (1.0 - base.r) * (1.0 - blend.r)); - result.g = base.g < 0.5 ? (2.0 * base.g * blend.g) : (1.0 - 2.0 * (1.0 - base.g) * (1.0 - blend.g)); - result.b = base.b < 0.5 ? (2.0 * base.b * blend.b) : (1.0 - 2.0 * (1.0 - base.b) * (1.0 - blend.b)); + result.r = base.r < 0.5 ? (2.0 * base.r * _blend.r) : (1.0 - 2.0 * (1.0 - base.r) * (1.0 - _blend.r)); + result.g = base.g < 0.5 ? (2.0 * base.g * _blend.g) : (1.0 - 2.0 * (1.0 - base.g) * (1.0 - _blend.g)); + result.b = base.b < 0.5 ? (2.0 * base.b * _blend.b) : (1.0 - 2.0 * (1.0 - base.b) * (1.0 - _blend.b)); return result; } - if (mode == DARKEN) return min(base, blend); - if (mode == LIGHTEN) return max(base, blend); - if (mode == COLOR_DODGE) return base / (1 - blend); - if (mode == COLOR_BURN) return 1 - (1 - base) / blend; + if (mode == DARKEN) return min(base, _blend); + if (mode == LIGHTEN) return max(base, _blend); + if (mode == COLOR_DODGE) return base / (1 - _blend); + if (mode == COLOR_BURN) return 1 - (1 - base) / _blend; if (mode == HARD_LIGHT) { vec3 result; - result.r = blend.r < 0.5 ? (2.0 * base.r * blend.r) : (1.0 - 2.0 * (1.0 - base.r) * (1.0 - blend.r)); - result.g = blend.g < 0.5 ? (2.0 * base.g * blend.g) : (1.0 - 2.0 * (1.0 - base.g) * (1.0 - blend.g)); - result.b = blend.b < 0.5 ? (2.0 * base.b * blend.b) : (1.0 - 2.0 * (1.0 - base.b) * (1.0 - blend.b)); + result.r = _blend.r < 0.5 ? (2.0 * base.r * _blend.r) : (1.0 - 2.0 * (1.0 - base.r) * (1.0 - _blend.r)); + result.g = _blend.g < 0.5 ? (2.0 * base.g * _blend.g) : (1.0 - 2.0 * (1.0 - base.g) * (1.0 - _blend.g)); + result.b = _blend.b < 0.5 ? (2.0 * base.b * _blend.b) : (1.0 - 2.0 * (1.0 - base.b) * (1.0 - _blend.b)); return result; } if (mode == SOFT_LIGHT) { - return vec3(soft_light_channel(base.r, blend.r), soft_light_channel(base.g, blend.g), soft_light_channel(base.b, blend.b)); + return vec3(soft_light_channel(base.r, _blend.r), soft_light_channel(base.g, _blend.g), soft_light_channel(base.b, _blend.b)); } - if (mode == DIFFERENCE) return abs(base - blend); - if (mode == EXCLUSION) return 0.5 - 2 * (base - 0.5) * (blend - 0.5); + if (mode == DIFFERENCE) return abs(base - _blend); + if (mode == EXCLUSION) return 0.5 - 2 * (base - 0.5) * (_blend - 0.5); if (mode == HUE) { vec3 base_hsl = rgb_to_hsl(base); - vec3 base_hsl = rgb_to_hsl(blend); + vec3 base_hsl = rgb_to_hsl(_blend); return hsl_to_rgb(vec3(base_hsl.x, base_hsl.y, base_hsl.z)); } if (mode == SATURATION) { vec3 base_hsl = rgb_to_hsl(base); - vec3 base_hsl = rgb_to_hsl(blend); + vec3 base_hsl = rgb_to_hsl(_blend); return hsl_to_rgb(vec3(base_hsl.x, base_hsl.y, base_hsl.z)); } if (mode == COLOR) { vec3 base_hsl = rgb_to_hsl(base); - vec3 base_hsl = rgb_to_hsl(blend); + vec3 base_hsl = rgb_to_hsl(_blend); return hsl_to_rgb(vec3(base_hsl.x, base_hsl.y, base_hsl.z)); } if (mode == LUMINOSITY) { vec3 base_hsl = rgb_to_hsl(base); - vec3 base_hsl = rgb_to_hsl(blend); + vec3 base_hsl = rgb_to_hsl(_blend); return hsl_to_rgb(vec3(base_hsl.x, base_hsl.y, base_hsl.z)); } - if (mode == PLUS_DARKER) return base + blend - 1; - if (mode == PLUS_LIGHTER) return base + blend; - return blend; // For NORMAL + if (mode == PLUS_DARKER) return base + _blend - 1; + if (mode == PLUS_LIGHTER) return base + _blend; + return _blend; // For NORMAL } void main() { vec4 c = texture2D(tex, cogl_tex_coord_in[0].st); vec3 pix_color = c.xyz; - vec3 color = blend(pix_color, vec3(red, green, blue)); + vec3 color = get_blend(pix_color, vec3(red, green, blue)); cogl_color_out = vec4(mix(pix_color, color, blend), 1.); } \ No newline at end of file From 78e77a42a278702b5744dd899141897fa840ec68 Mon Sep 17 00:00:00 2001 From: fractioneater Date: Sun, 28 Dec 2025 10:40:49 -0500 Subject: [PATCH 06/10] Now that's better --- src/effects/color.glsl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/effects/color.glsl b/src/effects/color.glsl index b85d79a5..f31ce233 100644 --- a/src/effects/color.glsl +++ b/src/effects/color.glsl @@ -79,23 +79,23 @@ vec3 get_blend(vec3 base, vec3 _blend) { if (mode == EXCLUSION) return 0.5 - 2 * (base - 0.5) * (_blend - 0.5); if (mode == HUE) { vec3 base_hsl = rgb_to_hsl(base); - vec3 base_hsl = rgb_to_hsl(_blend); - return hsl_to_rgb(vec3(base_hsl.x, base_hsl.y, base_hsl.z)); + vec3 blend_hsl = rgb_to_hsl(blend); + return hsl_to_rgb(vec3(blend_hsl.x, base_hsl.y, base_hsl.z)); } if (mode == SATURATION) { vec3 base_hsl = rgb_to_hsl(base); - vec3 base_hsl = rgb_to_hsl(_blend); - return hsl_to_rgb(vec3(base_hsl.x, base_hsl.y, base_hsl.z)); + vec3 blend_hsl = rgb_to_hsl(blend); + return hsl_to_rgb(vec3(base_hsl.x, blend_hsl.y, base_hsl.z)); } if (mode == COLOR) { vec3 base_hsl = rgb_to_hsl(base); - vec3 base_hsl = rgb_to_hsl(_blend); - return hsl_to_rgb(vec3(base_hsl.x, base_hsl.y, base_hsl.z)); + vec3 blend_hsl = rgb_to_hsl(blend); + return hsl_to_rgb(vec3(blend_hsl.x, blend_hsl.y, base_hsl.z)); } if (mode == LUMINOSITY) { vec3 base_hsl = rgb_to_hsl(base); - vec3 base_hsl = rgb_to_hsl(_blend); - return hsl_to_rgb(vec3(base_hsl.x, base_hsl.y, base_hsl.z)); + vec3 blend_hsl = rgb_to_hsl(blend); + return hsl_to_rgb(vec3(base_hsl.x, base_hsl.y, blend_hsl.z)); } if (mode == PLUS_DARKER) return base + _blend - 1; if (mode == PLUS_LIGHTER) return base + _blend; From 325d628a196a448a0a815eee01dc55ec49aa4aa9 Mon Sep 17 00:00:00 2001 From: fractioneater Date: Sun, 28 Dec 2025 10:42:22 -0500 Subject: [PATCH 07/10] Now it's ACTUALLY better --- src/effects/color.glsl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/effects/color.glsl b/src/effects/color.glsl index f31ce233..52de8109 100644 --- a/src/effects/color.glsl +++ b/src/effects/color.glsl @@ -79,22 +79,22 @@ vec3 get_blend(vec3 base, vec3 _blend) { if (mode == EXCLUSION) return 0.5 - 2 * (base - 0.5) * (_blend - 0.5); if (mode == HUE) { vec3 base_hsl = rgb_to_hsl(base); - vec3 blend_hsl = rgb_to_hsl(blend); + vec3 blend_hsl = rgb_to_hsl(_blend); return hsl_to_rgb(vec3(blend_hsl.x, base_hsl.y, base_hsl.z)); } if (mode == SATURATION) { vec3 base_hsl = rgb_to_hsl(base); - vec3 blend_hsl = rgb_to_hsl(blend); + vec3 blend_hsl = rgb_to_hsl(_blend); return hsl_to_rgb(vec3(base_hsl.x, blend_hsl.y, base_hsl.z)); } if (mode == COLOR) { vec3 base_hsl = rgb_to_hsl(base); - vec3 blend_hsl = rgb_to_hsl(blend); + vec3 blend_hsl = rgb_to_hsl(_blend); return hsl_to_rgb(vec3(blend_hsl.x, blend_hsl.y, base_hsl.z)); } if (mode == LUMINOSITY) { vec3 base_hsl = rgb_to_hsl(base); - vec3 blend_hsl = rgb_to_hsl(blend); + vec3 blend_hsl = rgb_to_hsl(_blend); return hsl_to_rgb(vec3(base_hsl.x, base_hsl.y, blend_hsl.z)); } if (mode == PLUS_DARKER) return base + _blend - 1; From 3da3677f6f47434424fc259619a8903ca97d1f7d Mon Sep 17 00:00:00 2001 From: fractioneater Date: Sun, 28 Dec 2025 10:57:05 -0500 Subject: [PATCH 08/10] Remove debugging line that didn't actually do anything --- src/effects/color.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/effects/color.js b/src/effects/color.js index 5af6b8b7..54188229 100644 --- a/src/effects/color.js +++ b/src/effects/color.js @@ -73,8 +73,6 @@ export const ColorEffect = utils.IS_IN_PREFERENCES ? this._blend = null; this._blend_mode = null; - console.log(`(${this._red}, ${this._green}, ${this._blue}, ${this._blend}) with mode ${this._blend_mode}`); //- TODO: REMOVE - // set shader source this._source = utils.get_shader_source(Shell, SHADER_FILENAME, import.meta.url); if (this._source) From f7c39b4b976c3ae5858e05d89b7a28b117c5c9b5 Mon Sep 17 00:00:00 2001 From: fractioneater Date: Sun, 28 Dec 2025 11:42:34 -0500 Subject: [PATCH 09/10] More intuitive order --- src/effects/color.glsl | 28 ++++++++++++++-------------- src/effects/color.js | 6 +++--- src/effects/effects.js | 6 +++--- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/effects/color.glsl b/src/effects/color.glsl index 52de8109..5df65121 100644 --- a/src/effects/color.glsl +++ b/src/effects/color.glsl @@ -11,18 +11,18 @@ const int SCREEN = 2; const int OVERLAY = 3; const int DARKEN = 4; const int LIGHTEN = 5; -const int COLOR_DODGE = 6; -const int COLOR_BURN = 7; -const int HARD_LIGHT = 8; -const int SOFT_LIGHT = 9; -const int DIFFERENCE = 10; -const int EXCLUSION = 11; -const int HUE = 12; -const int SATURATION = 13; -const int COLOR = 14; -const int LUMINOSITY = 15; -const int PLUS_DARKER = 16; -const int PLUS_LIGHTER = 17; +const int PLUS_DARKER = 6; +const int PLUS_LIGHTER = 7; +const int COLOR_DODGE = 8; +const int COLOR_BURN = 9; +const int HARD_LIGHT = 10; +const int SOFT_LIGHT = 11; +const int DIFFERENCE = 12; +const int EXCLUSION = 13; +const int HUE = 14; +const int SATURATION = 15; +const int COLOR = 16; +const int LUMINOSITY = 17; vec3 rgb_to_hsl(vec3 c) { vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); @@ -63,6 +63,8 @@ vec3 get_blend(vec3 base, vec3 _blend) { } if (mode == DARKEN) return min(base, _blend); if (mode == LIGHTEN) return max(base, _blend); + if (mode == PLUS_DARKER) return base + _blend - 1; + if (mode == PLUS_LIGHTER) return base + _blend; if (mode == COLOR_DODGE) return base / (1 - _blend); if (mode == COLOR_BURN) return 1 - (1 - base) / _blend; if (mode == HARD_LIGHT) { @@ -97,8 +99,6 @@ vec3 get_blend(vec3 base, vec3 _blend) { vec3 blend_hsl = rgb_to_hsl(_blend); return hsl_to_rgb(vec3(base_hsl.x, base_hsl.y, blend_hsl.z)); } - if (mode == PLUS_DARKER) return base + _blend - 1; - if (mode == PLUS_LIGHTER) return base + _blend; return _blend; // For NORMAL } diff --git a/src/effects/color.js b/src/effects/color.js index 54188229..ed47c9d6 100644 --- a/src/effects/color.js +++ b/src/effects/color.js @@ -58,9 +58,9 @@ export const ColorEffect = utils.IS_IN_PREFERENCES ? 0, ) } - // Normal (0), Multiply (1), Screen (2), Overlay (3), Darken (4), Lighten (5), Color dodge (6), Color burn (7), Hard light (8), - // Soft light (9), Difference (10), Exclusion (11), Hue (12), Saturation (13), Color (14), Luminosity (15), Plus darker (16), - // Plus lighter (17) + // Normal (0), Multiply (1), Screen (2), Overlay (3), Darken (4), Lighten (5), Plus darker (6), Plus lighter (7), Color dodge (8), + // Color burn (9), Hard light (10), Soft light (11), Difference (12), Exclusion (13), Hue (14), Saturation (15), Color (16), + // Luminosity (17) }, class ColorEffect extends Clutter.ShaderEffect { constructor(params) { // initialize without color as a parameter diff --git a/src/effects/effects.js b/src/effects/effects.js index 4ae8f4b4..e35588d6 100644 --- a/src/effects/effects.js +++ b/src/effects/effects.js @@ -180,6 +180,8 @@ export function get_supported_effects(_ = () => "") { _("Overlay"), _("Darken"), _("Lighten"), + _("Plus darker"), + _("Plus lighter"), _("Color dodge"), _("Color burn"), _("Hard light"), @@ -189,9 +191,7 @@ export function get_supported_effects(_ = () => "") { _("Hue"), _("Saturation"), _("Color"), - _("Luminosity"), - _("Plus darker"), - _("Plus lighter") + _("Luminosity") ] } } From 7d3c41db8fe34795ffa54ae3aa53a926100af317 Mon Sep 17 00:00:00 2001 From: fractioneater Date: Sun, 28 Dec 2025 14:04:56 -0500 Subject: [PATCH 10/10] So that's all I need to make it update --- src/effects/color.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/effects/color.js b/src/effects/color.js index ed47c9d6..1f4bdf4a 100644 --- a/src/effects/color.js +++ b/src/effects/color.js @@ -163,5 +163,6 @@ export const ColorEffect = utils.IS_IN_PREFERENCES ? /// False set function, only cares about the color. Too hard to change. set(params) { this.color = params.color; + this.blend_mode = params.blend_mode; } }); \ No newline at end of file