[gnome] Update extensions
This commit is contained in:
@ -1,114 +0,0 @@
|
||||
uniform sampler2D tex;
|
||||
uniform float sigma;
|
||||
uniform int dir;
|
||||
uniform float brightness;
|
||||
uniform float corner_radius;
|
||||
uniform float width;
|
||||
uniform float height;
|
||||
|
||||
float circleBounds(vec2 p, vec2 center, float clip_radius) {
|
||||
vec2 delta = p - center;
|
||||
float dist_squared = dot(delta, delta);
|
||||
|
||||
float outer_radius = clip_radius + 0.5;
|
||||
if (dist_squared >= (outer_radius * outer_radius))
|
||||
return 0.0;
|
||||
|
||||
float inner_radius = clip_radius - 0.5;
|
||||
if (dist_squared <= (inner_radius * inner_radius))
|
||||
return 1.0;
|
||||
|
||||
return outer_radius - sqrt(dist_squared);
|
||||
}
|
||||
|
||||
vec4 shapeCorner(vec4 pixel, vec2 p, vec2 center, float clip_radius) {
|
||||
float alpha = circleBounds(p, center, clip_radius);
|
||||
return vec4(pixel.rgb * alpha, min(alpha, pixel.a));
|
||||
}
|
||||
|
||||
vec4 getTexture(vec2 uv) {
|
||||
if (uv.x < 2. / width)
|
||||
uv.x = 2. / width;
|
||||
|
||||
if (uv.y < 2. / height)
|
||||
uv.y = 2. / height;
|
||||
|
||||
if (uv.x > 1. - 3. / width)
|
||||
uv.x = 1. - 3. / width;
|
||||
|
||||
if (uv.y > 1. - 3. / height)
|
||||
uv.y = 1. - 3. / height;
|
||||
|
||||
return texture2D(tex, uv);
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
vec2 uv = cogl_tex_coord_in[0].xy;
|
||||
|
||||
vec2 pos = uv * vec2(width, height);
|
||||
|
||||
vec2 direction = vec2(dir, (1.0 - dir));
|
||||
|
||||
float pixel_step;
|
||||
if (dir == 0)
|
||||
pixel_step = 1.0 / height;
|
||||
else
|
||||
pixel_step = 1.0 / width;
|
||||
|
||||
vec3 gauss_coefficient;
|
||||
gauss_coefficient.x = 1.0 / (sqrt(2.0 * 3.14159265) * sigma);
|
||||
gauss_coefficient.y = exp(-0.5 / (sigma * sigma));
|
||||
gauss_coefficient.z = gauss_coefficient.y * gauss_coefficient.y;
|
||||
|
||||
float gauss_coefficient_total = gauss_coefficient.x;
|
||||
|
||||
vec4 ret = getTexture(uv) * gauss_coefficient.x;
|
||||
gauss_coefficient.xy *= gauss_coefficient.yz;
|
||||
|
||||
int n_steps = int(ceil(1.5 * sigma)) * 2;
|
||||
|
||||
for (int i = 1; i <= n_steps; i += 2) {
|
||||
float coefficient_subtotal = gauss_coefficient.x;
|
||||
gauss_coefficient.xy *= gauss_coefficient.yz;
|
||||
coefficient_subtotal += gauss_coefficient.x;
|
||||
|
||||
float gauss_ratio = gauss_coefficient.x / coefficient_subtotal;
|
||||
|
||||
float foffset = float(i) + gauss_ratio;
|
||||
vec2 offset = direction * foffset * pixel_step;
|
||||
|
||||
ret += getTexture(uv + offset) * coefficient_subtotal;
|
||||
ret += getTexture(uv - offset) * coefficient_subtotal;
|
||||
|
||||
gauss_coefficient_total += 2.0 * coefficient_subtotal;
|
||||
gauss_coefficient.xy *= gauss_coefficient.yz;
|
||||
}
|
||||
vec4 outColor = ret / gauss_coefficient_total;
|
||||
|
||||
// apply brightness and rounding on the second pass (dir==0 comes last)
|
||||
if (dir == 0) {
|
||||
// left side
|
||||
if (pos.x < corner_radius) {
|
||||
// top left corner
|
||||
if (pos.y < corner_radius) {
|
||||
outColor = shapeCorner(outColor, pos, vec2(corner_radius + 2., corner_radius + 2.), corner_radius);
|
||||
// bottom left corner
|
||||
} else if (pos.y > height - corner_radius) {
|
||||
outColor = shapeCorner(outColor, pos, vec2(corner_radius + 2., height - corner_radius - 1.), corner_radius);
|
||||
}
|
||||
// right side
|
||||
} else if (pos.x > width - corner_radius) {
|
||||
// top right corner
|
||||
if (pos.y < corner_radius) {
|
||||
outColor = shapeCorner(outColor, pos, vec2(width - corner_radius - 1., corner_radius + 2.), corner_radius);
|
||||
// bottom right corner
|
||||
} else if (pos.y > height - corner_radius) {
|
||||
outColor = shapeCorner(outColor, pos, vec2(width - corner_radius - 1., height - corner_radius - 1.), corner_radius);
|
||||
}
|
||||
}
|
||||
|
||||
outColor.rgb *= brightness;
|
||||
}
|
||||
|
||||
cogl_color_out = outColor;
|
||||
}
|
||||
@ -1,236 +0,0 @@
|
||||
import GLib from 'gi://GLib';
|
||||
import GObject from 'gi://GObject';
|
||||
import Clutter from 'gi://Clutter';
|
||||
import Shell from 'gi://Shell';
|
||||
|
||||
const SHADER_PATH = GLib.filename_from_uri(GLib.uri_resolve_relative(import.meta.url, 'blur_effect.glsl', GLib.UriFlags.NONE))[0];
|
||||
|
||||
|
||||
const get_shader_source = _ => {
|
||||
try {
|
||||
return Shell.get_file_contents_utf8_sync(SHADER_PATH);
|
||||
} catch (e) {
|
||||
console.warn(`[Blur my Shell] error loading shader from ${SHADER_PATH}: ${e}`);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const BlurEffect = new GObject.registerClass({
|
||||
GTypeName: "BlurEffect",
|
||||
Properties: {
|
||||
'radius': GObject.ParamSpec.double(
|
||||
`radius`,
|
||||
`Radius`,
|
||||
`Blur radius`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, 2000.0,
|
||||
200.0,
|
||||
),
|
||||
'brightness': GObject.ParamSpec.double(
|
||||
`brightness`,
|
||||
`Brightness`,
|
||||
`Blur brightness`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, 1.0,
|
||||
0.6,
|
||||
),
|
||||
'width': GObject.ParamSpec.double(
|
||||
`width`,
|
||||
`Width`,
|
||||
`Width`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, Number.MAX_SAFE_INTEGER,
|
||||
0.0,
|
||||
),
|
||||
'height': GObject.ParamSpec.double(
|
||||
`height`,
|
||||
`Height`,
|
||||
`Height`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, Number.MAX_SAFE_INTEGER,
|
||||
0.0,
|
||||
),
|
||||
'corner_radius': GObject.ParamSpec.double(
|
||||
`corner_radius`,
|
||||
`Corner Radius`,
|
||||
`Corner Radius`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0, Number.MAX_SAFE_INTEGER,
|
||||
0,
|
||||
),
|
||||
'direction': GObject.ParamSpec.int(
|
||||
`direction`,
|
||||
`Direction`,
|
||||
`Direction`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0, 1,
|
||||
0,
|
||||
),
|
||||
'chained_effect': GObject.ParamSpec.object(
|
||||
`chained_effect`,
|
||||
`Chained Effect`,
|
||||
`Chained Effect`,
|
||||
GObject.ParamFlags.READABLE,
|
||||
GObject.Object,
|
||||
),
|
||||
}
|
||||
}, class BlurEffect extends Clutter.ShaderEffect {
|
||||
constructor(params, settings) {
|
||||
super(params);
|
||||
|
||||
this._sigma = null;
|
||||
this._brightness = null;
|
||||
this._width = null;
|
||||
this._height = null;
|
||||
this._corner_radius = null;
|
||||
|
||||
this._static = true;
|
||||
this._settings = settings;
|
||||
|
||||
this._direction = 0;
|
||||
|
||||
this._chained_effect = null;
|
||||
|
||||
if (params.sigma)
|
||||
this.sigma = params.sigma;
|
||||
if (params.brightness)
|
||||
this.brightness = params.brightness;
|
||||
if (params.width)
|
||||
this.width = params.width;
|
||||
if (params.height)
|
||||
this.height = params.height;
|
||||
if (params.corner_radius)
|
||||
this.corner_radius = params.corner_radius;
|
||||
if (params.direction)
|
||||
this.direction = params.direction;
|
||||
|
||||
// set shader source
|
||||
this._source = get_shader_source();
|
||||
if (this._source)
|
||||
this.set_shader_source(this._source);
|
||||
}
|
||||
|
||||
get radius() {
|
||||
return this._radius;
|
||||
}
|
||||
|
||||
set radius(value) {
|
||||
if (this._radius !== value) {
|
||||
this._radius = value;
|
||||
|
||||
// like Clutter, we use the assumption radius = 2*sigma
|
||||
this.set_uniform_value('sigma', parseFloat(this._radius / 2 - 1e-6));
|
||||
|
||||
if (this._chained_effect) {
|
||||
this._chained_effect.radius = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get brightness() {
|
||||
return this._brightness;
|
||||
}
|
||||
|
||||
set brightness(value) {
|
||||
if (this._brightness !== value) {
|
||||
this._brightness = value;
|
||||
|
||||
this.set_uniform_value('brightness', parseFloat(this._brightness - 1e-6));
|
||||
|
||||
if (this._chained_effect) {
|
||||
this._chained_effect.brightness = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get width() {
|
||||
return this._width;
|
||||
}
|
||||
|
||||
set width(value) {
|
||||
if (this._width !== value) {
|
||||
this._width = value;
|
||||
|
||||
this.set_uniform_value('width', parseFloat(this._width + 3.0 - 1e-6));
|
||||
|
||||
if (this._chained_effect) {
|
||||
this._chained_effect.width = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get height() {
|
||||
return this._height;
|
||||
}
|
||||
|
||||
set height(value) {
|
||||
if (this._height !== value) {
|
||||
this._height = value;
|
||||
|
||||
this.set_uniform_value('height', parseFloat(this._height + 3.0 - 1e-6));
|
||||
|
||||
if (this._chained_effect) {
|
||||
this._chained_effect.height = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get corner_radius() {
|
||||
return this._corner_radius;
|
||||
}
|
||||
|
||||
set corner_radius(value) {
|
||||
if (this._corner_radius !== value) {
|
||||
this._corner_radius = value;
|
||||
|
||||
this.set_uniform_value('corner_radius', parseFloat(this._corner_radius - 1e-6));
|
||||
|
||||
if (this._chained_effect) {
|
||||
this._chained_effect.corner_radius = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get direction() {
|
||||
return this._direction;
|
||||
}
|
||||
|
||||
set direction(value) {
|
||||
if (this._direction !== value) {
|
||||
this._direction = value;
|
||||
}
|
||||
}
|
||||
|
||||
get chained_effect() {
|
||||
return this._chained_effect;
|
||||
}
|
||||
|
||||
vfunc_set_actor(actor) {
|
||||
super.vfunc_set_actor(actor);
|
||||
|
||||
if (this._direction == 0) {
|
||||
this._chained_effect = new BlurEffect({
|
||||
radius: this.radius,
|
||||
brightness: this.brightness,
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
corner_radius: this.corner_radius,
|
||||
direction: 1
|
||||
});
|
||||
if (actor !== null)
|
||||
actor.add_effect(this._chained_effect);
|
||||
}
|
||||
}
|
||||
|
||||
vfunc_paint_target(paint_node = null, paint_context = null) {
|
||||
this.set_uniform_value("tex", 0);
|
||||
this.set_uniform_value("dir", this._direction);
|
||||
|
||||
if (paint_node && paint_context)
|
||||
super.vfunc_paint_target(paint_node, paint_context);
|
||||
else if (paint_node)
|
||||
super.vfunc_paint_target(paint_node);
|
||||
else
|
||||
super.vfunc_paint_target();
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,140 @@
|
||||
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, 0.0]
|
||||
};
|
||||
|
||||
|
||||
export const ColorEffect = utils.IS_IN_PREFERENCES ?
|
||||
{ default_params: DEFAULT_PARAMS } :
|
||||
new GObject.registerClass({
|
||||
GTypeName: "ColorEffect",
|
||||
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,
|
||||
),
|
||||
'blend': GObject.ParamSpec.double(
|
||||
`blend`,
|
||||
`Blend`,
|
||||
`Amount of blending between the colors`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, 1.0,
|
||||
0.0,
|
||||
),
|
||||
}
|
||||
}, class ColorEffect extends Clutter.ShaderEffect {
|
||||
constructor(params) {
|
||||
// initialize without color as a parameter
|
||||
const { color, ...parent_params } = params;
|
||||
super(parent_params);
|
||||
|
||||
this._red = null;
|
||||
this._green = null;
|
||||
this._blue = null;
|
||||
this._blend = 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);
|
||||
|
||||
// set shader color
|
||||
this.color = 'color' in params ? color : this.constructor.default_params.color;
|
||||
}
|
||||
|
||||
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 blend() {
|
||||
return this._blend;
|
||||
}
|
||||
|
||||
set blend(value) {
|
||||
if (this._blend !== value) {
|
||||
this._blend = value;
|
||||
|
||||
this.set_uniform_value('blend', parseFloat(this._blend - 1e-6));
|
||||
this.set_enabled(this.blend > 0);
|
||||
}
|
||||
}
|
||||
|
||||
set color(rgba) {
|
||||
let [r, g, b, a] = rgba;
|
||||
this.red = r;
|
||||
this.green = g;
|
||||
this.blue = b;
|
||||
this.blend = a;
|
||||
}
|
||||
|
||||
get color() {
|
||||
return [this.red, this.green, this.blue, this.blend];
|
||||
}
|
||||
|
||||
/// False set function, only cares about the color. Too hard to change.
|
||||
set(params) {
|
||||
this.color = params.color;
|
||||
}
|
||||
});
|
||||
@ -1,181 +0,0 @@
|
||||
import GLib from 'gi://GLib';
|
||||
import GObject from 'gi://GObject';
|
||||
import Clutter from 'gi://Clutter';
|
||||
import Shell from 'gi://Shell';
|
||||
|
||||
|
||||
const SHADER_PATH = GLib.filename_from_uri(GLib.uri_resolve_relative(import.meta.url, 'color_effect.glsl', GLib.UriFlags.NONE))[0];
|
||||
|
||||
|
||||
const get_shader_source = _ => {
|
||||
try {
|
||||
return Shell.get_file_contents_utf8_sync(SHADER_PATH);
|
||||
} catch (e) {
|
||||
console.warn(`[Blur my Shell] error loading shader from ${SHADER_PATH}: ${e}`);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/// New Clutter Shader Effect that simply mixes a color in, the class applies
|
||||
/// the GLSL shader programmed into vfunc_get_static_shader_source and applies
|
||||
/// it to an Actor.
|
||||
///
|
||||
/// Clutter Shader Source Code:
|
||||
/// https://github.com/GNOME/clutter/blob/master/clutter/clutter-shader-effect.c
|
||||
///
|
||||
/// GJS Doc:
|
||||
/// https://gjs-docs.gnome.org/clutter10~10_api/clutter.shadereffect
|
||||
export const ColorEffect = new GObject.registerClass({
|
||||
GTypeName: "ColorEffect",
|
||||
Properties: {
|
||||
'red': GObject.ParamSpec.double(
|
||||
`red`,
|
||||
`Red`,
|
||||
`Red value in shader`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, 1.0,
|
||||
0.4,
|
||||
),
|
||||
'green': GObject.ParamSpec.double(
|
||||
`green`,
|
||||
`Green`,
|
||||
`Green value in shader`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, 1.0,
|
||||
0.4,
|
||||
),
|
||||
'blue': GObject.ParamSpec.double(
|
||||
`blue`,
|
||||
`Blue`,
|
||||
`Blue value in shader`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, 1.0,
|
||||
0.4,
|
||||
),
|
||||
'blend': GObject.ParamSpec.double(
|
||||
`blend`,
|
||||
`Blend`,
|
||||
`Amount of blending between the colors`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, 1.0,
|
||||
0.4,
|
||||
),
|
||||
}
|
||||
}, class ColorShader extends Clutter.ShaderEffect {
|
||||
constructor(params, settings) {
|
||||
// initialize without color as a parameter
|
||||
let _color = params.color;
|
||||
delete params.color;
|
||||
|
||||
super(params);
|
||||
|
||||
this._red = null;
|
||||
this._green = null;
|
||||
this._blue = null;
|
||||
this._blend = null;
|
||||
|
||||
this._static = true;
|
||||
this._settings = settings;
|
||||
|
||||
// set shader source
|
||||
this._source = get_shader_source();
|
||||
if (this._source)
|
||||
this.set_shader_source(this._source);
|
||||
|
||||
// set shader color
|
||||
if (_color)
|
||||
this.color = _color;
|
||||
|
||||
this.update_enabled();
|
||||
}
|
||||
|
||||
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 blend() {
|
||||
return this._blend;
|
||||
}
|
||||
|
||||
set blend(value) {
|
||||
if (this._blend !== value) {
|
||||
this._blend = value;
|
||||
|
||||
this.set_uniform_value('blend', parseFloat(this._blend - 1e-6));
|
||||
}
|
||||
this.update_enabled();
|
||||
}
|
||||
|
||||
set color(rgba) {
|
||||
let [r, g, b, a] = rgba;
|
||||
this.red = r;
|
||||
this.green = g;
|
||||
this.blue = b;
|
||||
this.blend = a;
|
||||
}
|
||||
|
||||
get color() {
|
||||
return [this.red, this.green, this.blue, this.blend];
|
||||
}
|
||||
|
||||
/// False set function, only cares about the color. Too hard to change.
|
||||
set(params) {
|
||||
this.color = params.color;
|
||||
}
|
||||
|
||||
update_enabled() {
|
||||
// don't anything if this._settings is undefined (when calling super)
|
||||
if (this._settings === undefined)
|
||||
return;
|
||||
|
||||
this.set_enabled(
|
||||
this.blend > 0 &&
|
||||
this._settings.COLOR_AND_NOISE &&
|
||||
this._static
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
vfunc_paint_target(paint_node = null, paint_context = null) {
|
||||
this.set_uniform_value("tex", 0);
|
||||
|
||||
if (paint_node && paint_context)
|
||||
super.vfunc_paint_target(paint_node, paint_context);
|
||||
else if (paint_node)
|
||||
super.vfunc_paint_target(paint_node);
|
||||
else
|
||||
super.vfunc_paint_target();
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,93 @@
|
||||
// Heavily based on https://github.com/yilozt/rounded-window-corners
|
||||
// which is itself based on upstream Mutter code
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float radius;
|
||||
uniform float width;
|
||||
uniform float height;
|
||||
uniform bool corners_top;
|
||||
uniform bool corners_bottom;
|
||||
|
||||
uniform float clip_x0;
|
||||
uniform float clip_y0;
|
||||
uniform float clip_width;
|
||||
uniform float clip_height;
|
||||
|
||||
float circle_bounds(vec2 p, vec2 center, float clip_radius) {
|
||||
vec2 delta = p - center;
|
||||
float dist_squared = dot(delta, delta);
|
||||
|
||||
float outer_radius = clip_radius + 0.5;
|
||||
if (dist_squared >= (outer_radius * outer_radius))
|
||||
return 0.0;
|
||||
|
||||
float inner_radius = clip_radius - 0.5;
|
||||
if (dist_squared <= (inner_radius * inner_radius))
|
||||
return 1.0;
|
||||
|
||||
return outer_radius - sqrt(dist_squared);
|
||||
}
|
||||
|
||||
vec4 getTexture(vec2 uv) {
|
||||
if (uv.x < 2. / width)
|
||||
uv.x = 2. / width;
|
||||
|
||||
if (uv.y < 2. / height)
|
||||
uv.y = 2. / height;
|
||||
|
||||
if (uv.x > 1. - 3. / width)
|
||||
uv.x = 1. - 3. / width;
|
||||
|
||||
if (uv.y > 1. - 3. / height)
|
||||
uv.y = 1. - 3. / height;
|
||||
|
||||
return texture2D(tex, uv);
|
||||
}
|
||||
|
||||
float rounded_rect_coverage(vec2 p, vec4 bounds, float clip_radius) {
|
||||
// Outside the bounds
|
||||
if (p.x < bounds.x || p.x > bounds.z || p.y < bounds.y || p.y > bounds.w) {
|
||||
return 0.;
|
||||
}
|
||||
|
||||
vec2 center;
|
||||
|
||||
float center_left = bounds.x + clip_radius;
|
||||
float center_right = bounds.z - clip_radius;
|
||||
|
||||
if (p.x < center_left)
|
||||
center.x = center_left + 2.;
|
||||
else if (p.x > center_right)
|
||||
center.x = center_right - 1.;
|
||||
else
|
||||
return 1.0;
|
||||
|
||||
float center_top = bounds.y + clip_radius;
|
||||
float center_bottom = bounds.w - clip_radius;
|
||||
|
||||
if (corners_top && p.y < center_top)
|
||||
center.y = center_top + 2.;
|
||||
else if (corners_bottom && p.y > center_bottom)
|
||||
center.y = center_bottom - 1.;
|
||||
else
|
||||
return 1.0;
|
||||
|
||||
return circle_bounds(p, center, clip_radius);
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
vec2 uv = cogl_tex_coord_in[0].xy;
|
||||
vec2 pos = uv * vec2(width, height);
|
||||
vec4 c = getTexture(uv);
|
||||
|
||||
vec4 bounds;
|
||||
if (clip_width < 0. || clip_height < 0.) {
|
||||
bounds = vec4(clip_x0, clip_y0, clip_x0 + width, clip_y0 + height);
|
||||
} else {
|
||||
bounds = vec4(clip_x0, clip_y0, clip_x0 + clip_width, clip_y0 + clip_height);
|
||||
}
|
||||
|
||||
float alpha = rounded_rect_coverage(pos, bounds, radius);
|
||||
|
||||
cogl_color_out = vec4(c.rgb * alpha, min(alpha, c.a));
|
||||
}
|
||||
@ -0,0 +1,211 @@
|
||||
import GObject from 'gi://GObject';
|
||||
|
||||
import * as utils from '../conveniences/utils.js';
|
||||
const St = await utils.import_in_shell_only('gi://St');
|
||||
const Shell = await utils.import_in_shell_only('gi://Shell');
|
||||
const Clutter = await utils.import_in_shell_only('gi://Clutter');
|
||||
|
||||
const SHADER_FILENAME = 'corner.glsl';
|
||||
const DEFAULT_PARAMS = {
|
||||
radius: 12, width: 0, height: 0,
|
||||
corners_top: true, corners_bottom: true,
|
||||
clip: [0, 0, -1, -1]
|
||||
};
|
||||
|
||||
|
||||
export const CornerEffect = utils.IS_IN_PREFERENCES ?
|
||||
{ default_params: DEFAULT_PARAMS } :
|
||||
new GObject.registerClass({
|
||||
GTypeName: "CornerEffect",
|
||||
Properties: {
|
||||
'radius': GObject.ParamSpec.double(
|
||||
`radius`,
|
||||
`Corner Radius`,
|
||||
`Corner Radius`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0, Number.MAX_SAFE_INTEGER,
|
||||
12,
|
||||
),
|
||||
'width': GObject.ParamSpec.double(
|
||||
`width`,
|
||||
`Width`,
|
||||
`Width`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, Number.MAX_SAFE_INTEGER,
|
||||
0.0,
|
||||
),
|
||||
'height': GObject.ParamSpec.double(
|
||||
`height`,
|
||||
`Height`,
|
||||
`Height`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, Number.MAX_SAFE_INTEGER,
|
||||
0.0,
|
||||
),
|
||||
'corners_top': GObject.ParamSpec.boolean(
|
||||
`corners_top`,
|
||||
`Round top corners`,
|
||||
`Round top corners`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
true,
|
||||
),
|
||||
'corners_bottom': GObject.ParamSpec.boolean(
|
||||
`corners_bottom`,
|
||||
`Round bottom corners`,
|
||||
`Round bottom corners`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
true,
|
||||
),
|
||||
// FIXME this works but it logs an error, because I'm not a double...
|
||||
// I don't want to fiddle with GVariants again
|
||||
'clip': GObject.ParamSpec.double(
|
||||
`clip`,
|
||||
`Clip`,
|
||||
`Clip`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, Number.MAX_SAFE_INTEGER,
|
||||
0.0,
|
||||
),
|
||||
}
|
||||
}, class CornerEffect extends Clutter.ShaderEffect {
|
||||
constructor(params) {
|
||||
super(params);
|
||||
|
||||
this._clip_x0 = null;
|
||||
this._clip_y0 = null;
|
||||
this._clip_width = null;
|
||||
this._clip_height = 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);
|
||||
|
||||
const theme_context = St.ThemeContext.get_for_stage(global.stage);
|
||||
theme_context.connectObject('notify::scale-factor', _ => this.update_radius(), this);
|
||||
}
|
||||
|
||||
static get default_params() {
|
||||
return DEFAULT_PARAMS;
|
||||
}
|
||||
|
||||
get radius() {
|
||||
return this._radius;
|
||||
}
|
||||
|
||||
set radius(value) {
|
||||
if (this._radius !== value) {
|
||||
this._radius = value;
|
||||
|
||||
this.update_radius();
|
||||
}
|
||||
}
|
||||
|
||||
update_radius() {
|
||||
const theme_context = St.ThemeContext.get_for_stage(global.stage);
|
||||
let radius = Math.min(
|
||||
this.radius * theme_context.scale_factor,
|
||||
this.width / 2, this.height / 2
|
||||
);
|
||||
if (this._clip_width >= 0 || this._clip_height >= 0)
|
||||
radius = Math.min(radius, this._clip_width / 2, this._clip_height / 2);
|
||||
|
||||
this.set_uniform_value('radius', parseFloat(radius - 1e-6));
|
||||
}
|
||||
|
||||
get width() {
|
||||
return this._width;
|
||||
}
|
||||
|
||||
set width(value) {
|
||||
if (this._width !== value) {
|
||||
this._width = value;
|
||||
|
||||
this.set_uniform_value('width', parseFloat(this._width + 3.0 - 1e-6));
|
||||
this.update_radius();
|
||||
}
|
||||
}
|
||||
|
||||
get height() {
|
||||
return this._height;
|
||||
}
|
||||
|
||||
set height(value) {
|
||||
if (this._height !== value) {
|
||||
this._height = value;
|
||||
|
||||
this.set_uniform_value('height', parseFloat(this._height + 3.0 - 1e-6));
|
||||
this.update_radius();
|
||||
}
|
||||
}
|
||||
|
||||
get corners_top() {
|
||||
return this._corners_top;
|
||||
}
|
||||
|
||||
set corners_top(value) {
|
||||
if (this._corners_top !== value) {
|
||||
this._corners_top = value;
|
||||
|
||||
this.set_uniform_value('corners_top', this._corners_top ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
get corners_bottom() {
|
||||
return this._corners_bottom;
|
||||
}
|
||||
|
||||
set corners_bottom(value) {
|
||||
if (this._corners_bottom !== value) {
|
||||
this._corners_bottom = value;
|
||||
|
||||
this.set_uniform_value('corners_bottom', this._corners_bottom ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
get clip() {
|
||||
return [this._clip_x0, this._clip_y0, this._clip_width, this._clip_height];
|
||||
}
|
||||
|
||||
set clip(value) {
|
||||
[this._clip_x0, this._clip_y0, this._clip_width, this._clip_height] = value;
|
||||
this.set_uniform_value('clip_x0', parseFloat(this._clip_x0 - 1e-6));
|
||||
this.set_uniform_value('clip_y0', parseFloat(this._clip_y0 - 1e-6));
|
||||
this.set_uniform_value('clip_width', parseFloat(this._clip_width + 3 - 1e-6));
|
||||
this.set_uniform_value('clip_height', parseFloat(this._clip_height + 3 - 1e-6));
|
||||
this.update_radius();
|
||||
}
|
||||
|
||||
vfunc_set_actor(actor) {
|
||||
if (this._actor_connection_size_id) {
|
||||
let old_actor = this.get_actor();
|
||||
old_actor?.disconnect(this._actor_connection_size_id);
|
||||
}
|
||||
if (this._actor_connection_clip_rect_id) {
|
||||
let old_actor = this.get_actor();
|
||||
old_actor?.disconnect(this._actor_connection_clip_rect_id);
|
||||
}
|
||||
|
||||
if (actor) {
|
||||
this.width = actor.width;
|
||||
this.height = actor.height;
|
||||
this._actor_connection_size_id = actor.connect('notify::size', _ => {
|
||||
this.width = actor.width;
|
||||
this.height = actor.height;
|
||||
});
|
||||
|
||||
this.clip = actor.has_clip ? actor.get_clip() : [0, 0, -10, -10];
|
||||
this._actor_connection_clip_rect_id = actor.connect('notify::clip-rect', _ => {
|
||||
this.clip = actor.has_clip ? actor.get_clip() : [0, 0, -10, -10];
|
||||
});
|
||||
}
|
||||
else {
|
||||
this._actor_connection_size_id = null;
|
||||
this._actor_connection_clip_rect_id = null;
|
||||
}
|
||||
|
||||
super.vfunc_set_actor(actor);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,71 @@
|
||||
uniform sampler2D tex;
|
||||
uniform int operation;
|
||||
uniform float width;
|
||||
uniform float height;
|
||||
|
||||
#define CORRECTION 2.25
|
||||
#define SIZE_ADDITION 3
|
||||
|
||||
vec4 get_texture_at_position(vec2 position) {
|
||||
vec2 raw_position = position + vec2(CORRECTION, CORRECTION);
|
||||
vec2 raw_uv = raw_position / vec2(width + SIZE_ADDITION, height + SIZE_ADDITION);
|
||||
|
||||
return texture2D(tex, raw_uv);
|
||||
}
|
||||
|
||||
vec4 try_get_texture_at_position(vec2 position, inout int count) {
|
||||
if (any(greaterThanEqual(position, vec2(width, height))) ||
|
||||
any(lessThan(position, vec2(0, 0)))) {
|
||||
return vec4(0);
|
||||
} else {
|
||||
count++;
|
||||
return get_texture_at_position(position);
|
||||
}
|
||||
}
|
||||
|
||||
ivec2 get_corrected_position() {
|
||||
vec2 raw_uv = cogl_tex_coord0_in.st;
|
||||
vec2 raw_position = raw_uv * vec2(width + SIZE_ADDITION, height + SIZE_ADDITION);
|
||||
return ivec2(raw_position - vec2(CORRECTION, CORRECTION));
|
||||
}
|
||||
|
||||
void main() {
|
||||
ivec2 corrected_position = get_corrected_position();
|
||||
|
||||
// 1-step derivative
|
||||
if (operation == 0) {
|
||||
vec4 color = vec4(0);
|
||||
int c = 0;
|
||||
color += try_get_texture_at_position(corrected_position + vec2(0, 1), c);
|
||||
color -= try_get_texture_at_position(corrected_position + vec2(0, 0), c);
|
||||
color += try_get_texture_at_position(corrected_position + vec2(1, 0), c);
|
||||
color -= try_get_texture_at_position(corrected_position + vec2(0, 0), c);
|
||||
if (c < 4) {
|
||||
color = vec4(0);
|
||||
}
|
||||
cogl_color_out = vec4(color.xyz, 1);
|
||||
} else
|
||||
// 2-step derivative
|
||||
if (operation == 1) {
|
||||
vec4 color = vec4(0);
|
||||
int c = 0;
|
||||
color += try_get_texture_at_position(corrected_position + vec2(0, 1), c);
|
||||
color -= try_get_texture_at_position(corrected_position + vec2(0, -1), c);
|
||||
color += try_get_texture_at_position(corrected_position + vec2(1, 0), c);
|
||||
color -= try_get_texture_at_position(corrected_position + vec2(-1, 0), c);
|
||||
if (c < 4) {
|
||||
color = vec4(0);
|
||||
}
|
||||
cogl_color_out = vec4(color.xyz / 2, 1);
|
||||
} else
|
||||
// laplacian
|
||||
if (operation == 2) {
|
||||
vec4 color = vec4(0);
|
||||
color = -4 * get_texture_at_position(corrected_position);
|
||||
color += get_texture_at_position(corrected_position + vec2(0, 1));
|
||||
color += get_texture_at_position(corrected_position + vec2(0, -1));
|
||||
color += get_texture_at_position(corrected_position + vec2(1, 0));
|
||||
color += get_texture_at_position(corrected_position + vec2(-1, 0));
|
||||
cogl_color_out = vec4(color.xyz, 1);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,120 @@
|
||||
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 = 'derivative.glsl';
|
||||
const DEFAULT_PARAMS = {
|
||||
operation: 0, width: 0, height: 0
|
||||
};
|
||||
|
||||
|
||||
export const DerivativeEffect = utils.IS_IN_PREFERENCES ?
|
||||
{ default_params: DEFAULT_PARAMS } :
|
||||
new GObject.registerClass({
|
||||
GTypeName: "DerivativeEffect",
|
||||
Properties: {
|
||||
'operation': GObject.ParamSpec.int(
|
||||
`operation`,
|
||||
`Operation`,
|
||||
`Operation`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0, 2,
|
||||
0,
|
||||
),
|
||||
'width': GObject.ParamSpec.double(
|
||||
`width`,
|
||||
`Width`,
|
||||
`Width`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, Number.MAX_SAFE_INTEGER,
|
||||
0.0,
|
||||
),
|
||||
'height': GObject.ParamSpec.double(
|
||||
`height`,
|
||||
`Height`,
|
||||
`Height`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, Number.MAX_SAFE_INTEGER,
|
||||
0.0,
|
||||
)
|
||||
}
|
||||
}, class DerivativeEffect extends Clutter.ShaderEffect {
|
||||
constructor(params) {
|
||||
super(params);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static get default_params() {
|
||||
return DEFAULT_PARAMS;
|
||||
}
|
||||
|
||||
get operation() {
|
||||
return this._operation;
|
||||
}
|
||||
|
||||
set operation(value) {
|
||||
if (this._operation !== value) {
|
||||
this._operation = value;
|
||||
|
||||
this.set_uniform_value('operation', this._operation);
|
||||
}
|
||||
}
|
||||
|
||||
get width() {
|
||||
return this._width;
|
||||
}
|
||||
|
||||
set width(value) {
|
||||
if (this._width !== value) {
|
||||
this._width = value;
|
||||
|
||||
this.set_uniform_value('width', parseFloat(this._width - 1e-6));
|
||||
}
|
||||
}
|
||||
|
||||
get height() {
|
||||
return this._height;
|
||||
}
|
||||
|
||||
set height(value) {
|
||||
if (this._height !== value) {
|
||||
this._height = value;
|
||||
|
||||
this.set_uniform_value('height', parseFloat(this._height - 1e-6));
|
||||
}
|
||||
}
|
||||
|
||||
vfunc_set_actor(actor) {
|
||||
if (this._actor_connection_size_id) {
|
||||
let old_actor = this.get_actor();
|
||||
old_actor?.disconnect(this._actor_connection_size_id);
|
||||
}
|
||||
if (actor) {
|
||||
this.width = actor.width;
|
||||
this.height = actor.height;
|
||||
this._actor_connection_size_id = actor.connect('notify::size', _ => {
|
||||
this.width = actor.width;
|
||||
this.height = actor.height;
|
||||
});
|
||||
}
|
||||
else
|
||||
this._actor_connection_size_id = null;
|
||||
|
||||
super.vfunc_set_actor(actor);
|
||||
}
|
||||
|
||||
vfunc_paint_target(paint_node, paint_context) {
|
||||
// force setting nearest-neighbour texture filtering
|
||||
this.get_pipeline().set_layer_filters(0, 9728, 9728);
|
||||
|
||||
super.vfunc_paint_target(paint_node, paint_context);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,76 @@
|
||||
uniform sampler2D tex;
|
||||
uniform int divider;
|
||||
uniform float width;
|
||||
uniform float height;
|
||||
uniform int downsampling_mode;
|
||||
|
||||
#define CORRECTION 2.25
|
||||
#define SIZE_ADDITION 3
|
||||
|
||||
vec4 get_texture_at_position(vec2 position) {
|
||||
vec2 raw_position = position + vec2(CORRECTION, CORRECTION);
|
||||
vec2 raw_uv = raw_position / vec2(width + SIZE_ADDITION, height + SIZE_ADDITION);
|
||||
|
||||
return texture2D(tex, raw_uv);
|
||||
}
|
||||
|
||||
vec2 get_corrected_position() {
|
||||
vec2 raw_uv = cogl_tex_coord0_in.st;
|
||||
vec2 raw_position = raw_uv * vec2(width + SIZE_ADDITION, height + SIZE_ADDITION);
|
||||
return raw_position - vec2(CORRECTION, CORRECTION);
|
||||
}
|
||||
|
||||
void main() {
|
||||
ivec2 corrected_position = ivec2(get_corrected_position());
|
||||
vec2 multiplied_position = corrected_position * divider;
|
||||
|
||||
if (any(greaterThan(multiplied_position, vec2(width, height)))) {
|
||||
discard;
|
||||
}
|
||||
|
||||
// mode 0: boxcar downsampling
|
||||
if (downsampling_mode == 0) {
|
||||
|
||||
vec4 color = vec4(0.);
|
||||
int count = 0;
|
||||
for (int i = 0; i < divider; i++) {
|
||||
for (int j = 0; j < divider; j++) {
|
||||
vec2 lookup_position = multiplied_position + vec2(i, j);
|
||||
if (all(greaterThanEqual(lookup_position, vec2(0, 0))) &&
|
||||
all(lessThan(lookup_position, vec2(width, height)))) {
|
||||
color += get_texture_at_position(lookup_position);
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
cogl_color_out = color / count;
|
||||
|
||||
} else
|
||||
// mode 1: triangular downsampling
|
||||
if (downsampling_mode == 1) {
|
||||
|
||||
vec4 color = vec4(0.);
|
||||
int count = 0;
|
||||
int force = 1;
|
||||
for (int i = 0; i < divider; i++) {
|
||||
for (int j = 0; j < divider; j++) {
|
||||
vec2 lookup_position = multiplied_position + vec2(i, j);
|
||||
if (all(greaterThanEqual(lookup_position, vec2(0, 0))) &&
|
||||
all(lessThan(lookup_position, vec2(width, height)))) {
|
||||
force = 1 + divider - int(abs(divider - i - j));
|
||||
color += get_texture_at_position(lookup_position) * force;
|
||||
count += force;
|
||||
}
|
||||
}
|
||||
}
|
||||
cogl_color_out = color / count;
|
||||
|
||||
} else
|
||||
// mode 2: Dirac downsampling
|
||||
if (downsampling_mode == 2) {
|
||||
|
||||
vec2 lookup_position = min(multiplied_position + vec2(divider, divider) / 2, vec2(width - 1, height - 1));
|
||||
cogl_color_out = get_texture_at_position(lookup_position);
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,140 @@
|
||||
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 = 'downscale.glsl';
|
||||
const DEFAULT_PARAMS = {
|
||||
divider: 8, downsampling_mode: 0, width: 0, height: 0
|
||||
};
|
||||
|
||||
|
||||
export const DownscaleEffect = utils.IS_IN_PREFERENCES ?
|
||||
{ default_params: DEFAULT_PARAMS } :
|
||||
new GObject.registerClass({
|
||||
GTypeName: "DownscaleEffect",
|
||||
Properties: {
|
||||
'divider': GObject.ParamSpec.int(
|
||||
`divider`,
|
||||
`Divider`,
|
||||
`Divider`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0, 64,
|
||||
8,
|
||||
),
|
||||
'downsampling_mode': GObject.ParamSpec.int(
|
||||
`downsampling_mode`,
|
||||
`Downsampling mode`,
|
||||
`Downsampling mode`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0, 2,
|
||||
0,
|
||||
),
|
||||
'width': GObject.ParamSpec.double(
|
||||
`width`,
|
||||
`Width`,
|
||||
`Width`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, Number.MAX_SAFE_INTEGER,
|
||||
0.0,
|
||||
),
|
||||
'height': GObject.ParamSpec.double(
|
||||
`height`,
|
||||
`Height`,
|
||||
`Height`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, Number.MAX_SAFE_INTEGER,
|
||||
0.0,
|
||||
)
|
||||
}
|
||||
}, class DownscaleEffect extends Clutter.ShaderEffect {
|
||||
constructor(params) {
|
||||
super(params);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static get default_params() {
|
||||
return DEFAULT_PARAMS;
|
||||
}
|
||||
|
||||
get divider() {
|
||||
return this._divider;
|
||||
}
|
||||
|
||||
set divider(value) {
|
||||
if (this._divider !== value) {
|
||||
this._divider = value;
|
||||
|
||||
this.set_uniform_value('divider', this._divider);
|
||||
}
|
||||
}
|
||||
|
||||
get downsampling_mode() {
|
||||
return this._downsampling_mode;
|
||||
}
|
||||
|
||||
set downsampling_mode(value) {
|
||||
if (this._downsampling_mode !== value) {
|
||||
this._downsampling_mode = value;
|
||||
|
||||
this.set_uniform_value('downsampling_mode', this._downsampling_mode);
|
||||
}
|
||||
}
|
||||
|
||||
get width() {
|
||||
return this._width;
|
||||
}
|
||||
|
||||
set width(value) {
|
||||
if (this._width !== value) {
|
||||
this._width = value;
|
||||
|
||||
this.set_uniform_value('width', parseFloat(this._width - 1e-6));
|
||||
}
|
||||
}
|
||||
|
||||
get height() {
|
||||
return this._height;
|
||||
}
|
||||
|
||||
set height(value) {
|
||||
if (this._height !== value) {
|
||||
this._height = value;
|
||||
|
||||
this.set_uniform_value('height', parseFloat(this._height - 1e-6));
|
||||
}
|
||||
}
|
||||
|
||||
vfunc_set_actor(actor) {
|
||||
if (this._actor_connection_size_id) {
|
||||
let old_actor = this.get_actor();
|
||||
old_actor?.disconnect(this._actor_connection_size_id);
|
||||
}
|
||||
if (actor) {
|
||||
this.width = actor.width;
|
||||
this.height = actor.height;
|
||||
this._actor_connection_size_id = actor.connect('notify::size', _ => {
|
||||
this.width = actor.width;
|
||||
this.height = actor.height;
|
||||
});
|
||||
}
|
||||
else
|
||||
this._actor_connection_size_id = null;
|
||||
|
||||
super.vfunc_set_actor(actor);
|
||||
}
|
||||
|
||||
vfunc_paint_target(paint_node, paint_context) {
|
||||
// force setting nearest-neighbour texture filtering
|
||||
this.get_pipeline().set_layer_filters(0, 9728, 9728);
|
||||
|
||||
super.vfunc_paint_target(paint_node, paint_context);
|
||||
}
|
||||
});
|
||||
332
gnome/.local/share/gnome-shell/extensions/blur-my-shell@aunetx/effects/effects.js
vendored
Normal file
332
gnome/.local/share/gnome-shell/extensions/blur-my-shell@aunetx/effects/effects.js
vendored
Normal file
@ -0,0 +1,332 @@
|
||||
import { NativeDynamicBlurEffect } from '../effects/native_dynamic_gaussian_blur.js';
|
||||
import { NativeStaticBlurEffect } from '../effects/native_static_gaussian_blur.js';
|
||||
import { GaussianBlurEffect } from '../effects/gaussian_blur.js';
|
||||
import { MonteCarloBlurEffect } from '../effects/monte_carlo_blur.js';
|
||||
import { ColorEffect } from '../effects/color.js';
|
||||
import { NoiseEffect } from '../effects/noise.js';
|
||||
import { CornerEffect } from '../effects/corner.js';
|
||||
import { DownscaleEffect } from './downscale.js';
|
||||
import { UpscaleEffect } from './upscale.js';
|
||||
import { PixelizeEffect } from './pixelize.js';
|
||||
import { DerivativeEffect } from './derivative.js';
|
||||
import { RgbToHslEffect } from './rgb_to_hsl.js';
|
||||
import { HslToRgbEffect } from './hsl_to_rgb.js';
|
||||
|
||||
// We do in this way because I've not found another way to store our preferences in a dictionnary
|
||||
// while calling `gettext` on it while in preferences. Not so pretty, but works.
|
||||
export function get_effects_groups(_ = _ => "") {
|
||||
return {
|
||||
blur_effects: {
|
||||
name: _("Blur effects"),
|
||||
contains: [
|
||||
"native_static_gaussian_blur",
|
||||
"gaussian_blur",
|
||||
"monte_carlo_blur"
|
||||
]
|
||||
},
|
||||
texture_effects: {
|
||||
name: _("Texture effects"),
|
||||
contains: [
|
||||
"downscale",
|
||||
"upscale",
|
||||
"pixelize",
|
||||
"derivative",
|
||||
"noise",
|
||||
"color",
|
||||
"rgb_to_hsl",
|
||||
"hsl_to_rgb"
|
||||
]
|
||||
},
|
||||
shape_effects: {
|
||||
name: _("Shape effects"),
|
||||
contains: [
|
||||
"corner"
|
||||
]
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export function get_supported_effects(_ = () => "") {
|
||||
return {
|
||||
native_dynamic_gaussian_blur: {
|
||||
class: NativeDynamicBlurEffect
|
||||
},
|
||||
|
||||
native_static_gaussian_blur: {
|
||||
class: NativeStaticBlurEffect,
|
||||
name: _("Native gaussian blur"),
|
||||
description: _("An optimized blur effect that smoothly blends pixels within a given radius."),
|
||||
is_advanced: false,
|
||||
editable_params: {
|
||||
unscaled_radius: {
|
||||
name: _("Radius"),
|
||||
description: _("The intensity of the blur effect."),
|
||||
type: "float",
|
||||
min: 0.,
|
||||
max: 100.,
|
||||
increment: 1.0,
|
||||
big_increment: 10.,
|
||||
digits: 0
|
||||
},
|
||||
brightness: {
|
||||
name: _("Brightness"),
|
||||
description: _("The brightness of the blur effect, a high value might make the text harder to read."),
|
||||
type: "float",
|
||||
min: 0.,
|
||||
max: 1.,
|
||||
increment: 0.01,
|
||||
big_increment: 0.1,
|
||||
digits: 2
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
gaussian_blur: {
|
||||
class: GaussianBlurEffect,
|
||||
name: _("Gaussian blur (advanced effect)"),
|
||||
description: _("A blur effect that smoothly blends pixels within a given radius. This effect is more precise, but way less optimized."),
|
||||
is_advanced: true,
|
||||
editable_params: {
|
||||
radius: {
|
||||
name: _("Radius"),
|
||||
description: _("The intensity of the blur effect. The bigger it is, the slower it will be."),
|
||||
type: "float",
|
||||
min: 0.,
|
||||
max: 100.,
|
||||
increment: .1,
|
||||
big_increment: 10.,
|
||||
digits: 1
|
||||
},
|
||||
brightness: {
|
||||
name: _("Brightness"),
|
||||
description: _("The brightness of the blur effect, a high value might make the text harder to read."),
|
||||
type: "float",
|
||||
min: 0.,
|
||||
max: 1.,
|
||||
increment: 0.01,
|
||||
big_increment: 0.1,
|
||||
digits: 2
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
monte_carlo_blur: {
|
||||
class: MonteCarloBlurEffect,
|
||||
name: _("Monte Carlo blur"),
|
||||
description: _("A blur effect that mimics a random walk, by picking pixels further and further away from its origin and mixing them all together."),
|
||||
is_advanced: false,
|
||||
editable_params: {
|
||||
radius: {
|
||||
name: _("Radius"),
|
||||
description: _("The maximum travel distance for each step in the random walk. A higher value will make the blur more randomized."),
|
||||
type: "float",
|
||||
min: 0.,
|
||||
max: 10.,
|
||||
increment: 0.01,
|
||||
big_increment: 0.1,
|
||||
digits: 2
|
||||
},
|
||||
iterations: {
|
||||
name: _("Iterations"),
|
||||
description: _("The number of iterations. The more there are, the smoother the blur is."),
|
||||
type: "integer",
|
||||
min: 0,
|
||||
max: 50,
|
||||
increment: 1
|
||||
},
|
||||
brightness: {
|
||||
name: _("Brightness"),
|
||||
description: _("The brightness of the blur effect, a high value might make the text harder to read."),
|
||||
type: "float",
|
||||
min: 0.,
|
||||
max: 1.,
|
||||
increment: 0.01,
|
||||
big_increment: 0.1,
|
||||
digits: 2
|
||||
},
|
||||
use_base_pixel: {
|
||||
name: _("Use base pixel"),
|
||||
description: _("Whether or not the original pixel is counted for the blur. If it is, the image will be more legible."),
|
||||
type: "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
color: {
|
||||
class: ColorEffect,
|
||||
name: _("Color"),
|
||||
description: _("An effect that blends a color into the pipeline."),
|
||||
is_advanced: false,
|
||||
// TODO make this RGB + blend
|
||||
editable_params: {
|
||||
color: {
|
||||
name: _("Color"),
|
||||
description: _("The color to blend in. The blending amount is controled by the opacity of the color."),
|
||||
type: "rgba"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
pixelize: {
|
||||
class: PixelizeEffect,
|
||||
name: _("Pixelize"),
|
||||
description: _("An effect that pixelizes the image."),
|
||||
is_advanced: false,
|
||||
editable_params: {
|
||||
factor: {
|
||||
name: _("Factor"),
|
||||
description: _("How much to scale down the image."),
|
||||
type: "integer",
|
||||
min: 1,
|
||||
max: 50,
|
||||
increment: 1
|
||||
},
|
||||
downsampling_mode: {
|
||||
name: _("Downsampling mode"),
|
||||
description: _("The downsampling method that is used."),
|
||||
type: "dropdown",
|
||||
options: [
|
||||
_("Boxcar"),
|
||||
_("Triangular"),
|
||||
_("Dirac")
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
downscale: {
|
||||
class: DownscaleEffect,
|
||||
name: _("Downscale (advanced effect)"),
|
||||
description: _("An effect that downscales the image and put it on the top-left corner."),
|
||||
is_advanced: true,
|
||||
editable_params: {
|
||||
divider: {
|
||||
name: _("Factor"),
|
||||
description: _("How much to scale down the image."),
|
||||
type: "integer",
|
||||
min: 1,
|
||||
max: 50,
|
||||
increment: 1
|
||||
},
|
||||
downsampling_mode: {
|
||||
name: _("Downsampling mode"),
|
||||
description: _("The downsampling method that is used."),
|
||||
type: "dropdown",
|
||||
options: [
|
||||
_("Boxcar"),
|
||||
_("Triangular"),
|
||||
_("Dirac")
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
upscale: {
|
||||
class: UpscaleEffect,
|
||||
name: _("Upscale (advanced effect)"),
|
||||
description: _("An effect that upscales the image from the top-left corner."),
|
||||
is_advanced: true,
|
||||
editable_params: {
|
||||
factor: {
|
||||
name: _("Factor"),
|
||||
description: _("How much to scale up the image."),
|
||||
type: "integer",
|
||||
min: 1,
|
||||
max: 50,
|
||||
increment: 1
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
derivative: {
|
||||
class: DerivativeEffect,
|
||||
name: _("Derivative"),
|
||||
description: _("Apply a spatial derivative, or a laplacian."),
|
||||
is_advanced: false,
|
||||
editable_params: {
|
||||
operation: {
|
||||
name: _("Operation"),
|
||||
description: _("The mathematical operation to apply."),
|
||||
type: "dropdown",
|
||||
options: [
|
||||
_("1-step derivative"),
|
||||
_("2-step derivative"),
|
||||
_("Laplacian")
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
noise: {
|
||||
class: NoiseEffect,
|
||||
name: _("Noise"),
|
||||
description: _("An effect that adds a random noise. Prefer the Monte Carlo blur for a more organic effect if needed."),
|
||||
is_advanced: false,
|
||||
editable_params: {
|
||||
noise: {
|
||||
name: _("Noise"),
|
||||
description: _("The amount of noise to add."),
|
||||
type: "float",
|
||||
min: 0.,
|
||||
max: 1.,
|
||||
increment: 0.01,
|
||||
big_increment: 0.1,
|
||||
digits: 2
|
||||
},
|
||||
lightness: {
|
||||
name: _("Lightness"),
|
||||
description: _("The luminosity of the noise. A setting of '1.0' will make the effect transparent."),
|
||||
type: "float",
|
||||
min: 0.,
|
||||
max: 2.,
|
||||
increment: 0.01,
|
||||
big_increment: 0.1,
|
||||
digits: 2
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
rgb_to_hsl: {
|
||||
class: RgbToHslEffect,
|
||||
name: _("RGB to HSL (advanced effect)"),
|
||||
description: _("Converts the image from RGBA colorspace to HSLA."),
|
||||
is_advanced: true,
|
||||
editable_params: {}
|
||||
},
|
||||
|
||||
hsl_to_rgb: {
|
||||
class: HslToRgbEffect,
|
||||
name: _("HSL to RGB (advanced effect)"),
|
||||
description: _("Converts the image from HSLA colorspace to RGBA."),
|
||||
is_advanced: true,
|
||||
editable_params: {}
|
||||
},
|
||||
|
||||
corner: {
|
||||
class: CornerEffect,
|
||||
name: _("Corner"),
|
||||
description: _("An effect that draws corners. Add it last not to have the other effects perturb the corners."),
|
||||
is_advanced: false,
|
||||
editable_params: {
|
||||
radius: {
|
||||
name: _("Radius"),
|
||||
description: _("The radius of the corner. GNOME apps use a radius of 12 px by default."),
|
||||
type: "integer",
|
||||
min: 0,
|
||||
max: 50,
|
||||
increment: 1,
|
||||
},
|
||||
corners_top: {
|
||||
name: _("Top corners"),
|
||||
description: _("Whether or not to round the top corners."),
|
||||
type: "boolean"
|
||||
},
|
||||
corners_bottom: {
|
||||
name: _("Bottom corners"),
|
||||
description: _("Whether or not to round the bottom corners."),
|
||||
type: "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,70 @@
|
||||
uniform sampler2D tex;
|
||||
uniform float sigma;
|
||||
uniform int dir;
|
||||
uniform float brightness;
|
||||
uniform float width;
|
||||
uniform float height;
|
||||
|
||||
vec4 getTexture(vec2 uv) {
|
||||
if (uv.x < 3. / width)
|
||||
uv.x = 3. / width;
|
||||
|
||||
if (uv.y < 3. / height)
|
||||
uv.y = 3. / height;
|
||||
|
||||
if (uv.x > 1. - 3. / width)
|
||||
uv.x = 1. - 3. / width;
|
||||
|
||||
if (uv.y > 1. - 3. / height)
|
||||
uv.y = 1. - 3. / height;
|
||||
|
||||
return texture2D(tex, uv);
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
vec2 uv = cogl_tex_coord_in[0].xy;
|
||||
vec2 direction = vec2(dir, (1.0 - dir));
|
||||
|
||||
float pixel_step;
|
||||
if (dir == 0)
|
||||
pixel_step = 1.0 / height;
|
||||
else
|
||||
pixel_step = 1.0 / width;
|
||||
|
||||
vec3 gauss_coefficient;
|
||||
gauss_coefficient.x = 1.0 / (sqrt(2.0 * 3.14159265) * sigma);
|
||||
gauss_coefficient.y = exp(-0.5 / (sigma * sigma));
|
||||
gauss_coefficient.z = gauss_coefficient.y * gauss_coefficient.y;
|
||||
|
||||
float gauss_coefficient_total = gauss_coefficient.x;
|
||||
|
||||
vec4 ret = getTexture(uv) * gauss_coefficient.x;
|
||||
gauss_coefficient.xy *= gauss_coefficient.yz;
|
||||
|
||||
int n_steps = int(ceil(1.5 * sigma)) * 2;
|
||||
|
||||
for (int i = 1; i <= n_steps; i += 2) {
|
||||
float coefficient_subtotal = gauss_coefficient.x;
|
||||
gauss_coefficient.xy *= gauss_coefficient.yz;
|
||||
coefficient_subtotal += gauss_coefficient.x;
|
||||
|
||||
float gauss_ratio = gauss_coefficient.x / coefficient_subtotal;
|
||||
|
||||
float foffset = float(i) + gauss_ratio;
|
||||
vec2 offset = direction * foffset * pixel_step;
|
||||
|
||||
ret += getTexture(uv + offset) * coefficient_subtotal;
|
||||
ret += getTexture(uv - offset) * coefficient_subtotal;
|
||||
|
||||
gauss_coefficient_total += 2.0 * coefficient_subtotal;
|
||||
gauss_coefficient.xy *= gauss_coefficient.yz;
|
||||
}
|
||||
vec4 outColor = ret / gauss_coefficient_total;
|
||||
|
||||
// apply brightness on the second pass (dir==0 comes last)
|
||||
if (dir == 0) {
|
||||
outColor.rgb *= brightness;
|
||||
}
|
||||
|
||||
cogl_color_out = outColor;
|
||||
}
|
||||
@ -0,0 +1,213 @@
|
||||
import GObject from 'gi://GObject';
|
||||
|
||||
import * as utils from '../conveniences/utils.js';
|
||||
const St = await utils.import_in_shell_only('gi://St');
|
||||
const Shell = await utils.import_in_shell_only('gi://Shell');
|
||||
const Clutter = await utils.import_in_shell_only('gi://Clutter');
|
||||
|
||||
const SHADER_FILENAME = 'gaussian_blur.glsl';
|
||||
const DEFAULT_PARAMS = {
|
||||
radius: 30, brightness: .6,
|
||||
width: 0, height: 0, direction: 0, chained_effect: null
|
||||
};
|
||||
|
||||
|
||||
export const GaussianBlurEffect = utils.IS_IN_PREFERENCES ?
|
||||
{ default_params: DEFAULT_PARAMS } :
|
||||
new GObject.registerClass({
|
||||
GTypeName: "GaussianBlurEffect",
|
||||
Properties: {
|
||||
'radius': GObject.ParamSpec.double(
|
||||
`radius`,
|
||||
`Radius`,
|
||||
`Blur radius`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, 2000.0,
|
||||
30.0,
|
||||
),
|
||||
'brightness': GObject.ParamSpec.double(
|
||||
`brightness`,
|
||||
`Brightness`,
|
||||
`Blur brightness`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, 1.0,
|
||||
0.6,
|
||||
),
|
||||
'width': GObject.ParamSpec.double(
|
||||
`width`,
|
||||
`Width`,
|
||||
`Width`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, Number.MAX_SAFE_INTEGER,
|
||||
0.0,
|
||||
),
|
||||
'height': GObject.ParamSpec.double(
|
||||
`height`,
|
||||
`Height`,
|
||||
`Height`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, Number.MAX_SAFE_INTEGER,
|
||||
0.0,
|
||||
),
|
||||
'direction': GObject.ParamSpec.int(
|
||||
`direction`,
|
||||
`Direction`,
|
||||
`Direction`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0, 1,
|
||||
0,
|
||||
),
|
||||
'chained_effect': GObject.ParamSpec.object(
|
||||
`chained_effect`,
|
||||
`Chained Effect`,
|
||||
`Chained Effect`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
GObject.Object,
|
||||
),
|
||||
}
|
||||
}, class GaussianBlurEffect extends Clutter.ShaderEffect {
|
||||
constructor(params) {
|
||||
super(params);
|
||||
|
||||
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);
|
||||
|
||||
const theme_context = St.ThemeContext.get_for_stage(global.stage);
|
||||
theme_context.connectObject(
|
||||
'notify::scale-factor', _ =>
|
||||
this.set_uniform_value('sigma',
|
||||
parseFloat(this.radius * theme_context.scale_factor / 2 - 1e-6)
|
||||
),
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
static get default_params() {
|
||||
return DEFAULT_PARAMS;
|
||||
}
|
||||
|
||||
get radius() {
|
||||
return this._radius;
|
||||
}
|
||||
|
||||
set radius(value) {
|
||||
if (this._radius !== value) {
|
||||
this._radius = value;
|
||||
|
||||
const scale_factor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
|
||||
// like Clutter, we use the assumption radius = 2*sigma
|
||||
this.set_uniform_value('sigma', parseFloat(this._radius * scale_factor / 2 - 1e-6));
|
||||
this.set_enabled(this.radius > 0.);
|
||||
|
||||
if (this.chained_effect)
|
||||
this.chained_effect.radius = value;
|
||||
}
|
||||
}
|
||||
|
||||
get brightness() {
|
||||
return this._brightness;
|
||||
}
|
||||
|
||||
set brightness(value) {
|
||||
if (this._brightness !== value) {
|
||||
this._brightness = value;
|
||||
|
||||
this.set_uniform_value('brightness', parseFloat(this._brightness - 1e-6));
|
||||
|
||||
if (this.chained_effect)
|
||||
this.chained_effect.brightness = value;
|
||||
}
|
||||
}
|
||||
|
||||
get width() {
|
||||
return this._width;
|
||||
}
|
||||
|
||||
set width(value) {
|
||||
if (this._width !== value) {
|
||||
this._width = value;
|
||||
|
||||
this.set_uniform_value('width', parseFloat(this._width + 3.0 - 1e-6));
|
||||
|
||||
if (this.chained_effect)
|
||||
this.chained_effect.width = value;
|
||||
}
|
||||
}
|
||||
|
||||
get height() {
|
||||
return this._height;
|
||||
}
|
||||
|
||||
set height(value) {
|
||||
if (this._height !== value) {
|
||||
this._height = value;
|
||||
|
||||
this.set_uniform_value('height', parseFloat(this._height + 3.0 - 1e-6));
|
||||
|
||||
if (this.chained_effect)
|
||||
this.chained_effect.height = value;
|
||||
}
|
||||
}
|
||||
|
||||
get direction() {
|
||||
return this._direction;
|
||||
}
|
||||
|
||||
set direction(value) {
|
||||
if (this._direction !== value)
|
||||
this._direction = value;
|
||||
}
|
||||
|
||||
get chained_effect() {
|
||||
return this._chained_effect;
|
||||
}
|
||||
|
||||
set chained_effect(value) {
|
||||
this._chained_effect = value;
|
||||
}
|
||||
|
||||
vfunc_set_actor(actor) {
|
||||
if (this._actor_connection_size_id) {
|
||||
let old_actor = this.get_actor();
|
||||
old_actor?.disconnect(this._actor_connection_size_id);
|
||||
}
|
||||
if (actor) {
|
||||
this.width = actor.width;
|
||||
this.height = actor.height;
|
||||
this._actor_connection_size_id = actor.connect('notify::size', _ => {
|
||||
this.width = actor.width;
|
||||
this.height = actor.height;
|
||||
});
|
||||
}
|
||||
else
|
||||
this._actor_connection_size_id = null;
|
||||
|
||||
super.vfunc_set_actor(actor);
|
||||
|
||||
if (this.direction == 0) {
|
||||
if (this.chained_effect)
|
||||
this.chained_effect.get_actor()?.remove_effect(this.chained_effect);
|
||||
else
|
||||
this.chained_effect = new GaussianBlurEffect({
|
||||
radius: this.radius,
|
||||
brightness: this.brightness,
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
direction: 1
|
||||
});
|
||||
if (actor !== null)
|
||||
actor.add_effect(this.chained_effect);
|
||||
}
|
||||
}
|
||||
|
||||
vfunc_paint_target(paint_node, paint_context) {
|
||||
this.set_uniform_value("dir", this.direction);
|
||||
|
||||
super.vfunc_paint_target(paint_node, paint_context);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,14 @@
|
||||
uniform sampler2D tex;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
vec2 uv = cogl_tex_coord_in[0].xy;
|
||||
vec4 hsla = texture2D(tex, uv);
|
||||
vec4 rgba = vec4(hsl_to_rgb(hsla.xyz), hsla.w);
|
||||
cogl_color_out = rgba;
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
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 = 'hsl_to_rgb.glsl';
|
||||
const DEFAULT_PARAMS = {};
|
||||
|
||||
|
||||
export const HslToRgbEffect = utils.IS_IN_PREFERENCES ?
|
||||
{ default_params: DEFAULT_PARAMS } :
|
||||
new GObject.registerClass({
|
||||
GTypeName: "HslToRgbEffect",
|
||||
Properties: {}
|
||||
}, class HslToRgbEffect extends Clutter.ShaderEffect {
|
||||
constructor(params) {
|
||||
super(params);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static get default_params() {
|
||||
return DEFAULT_PARAMS;
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,44 @@
|
||||
uniform sampler2D tex;
|
||||
uniform float radius;
|
||||
uniform int iterations;
|
||||
uniform float brightness;
|
||||
uniform float width;
|
||||
uniform float height;
|
||||
uniform bool use_base_pixel;
|
||||
|
||||
float srand(vec2 a) {
|
||||
return sin(dot(a, vec2(1233.224, 1743.335)));
|
||||
}
|
||||
|
||||
float rand(inout float r) {
|
||||
r = fract(3712.65 * r + 0.61432);
|
||||
return (r - 0.5) * 2.0;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 uv = cogl_tex_coord0_in.st;
|
||||
vec2 p = 16 * radius / vec2(width, height);
|
||||
float r = srand(uv);
|
||||
vec2 rv;
|
||||
|
||||
int count = 0;
|
||||
vec4 c = vec4(0.);
|
||||
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
rv.x = rand(r);
|
||||
rv.y = rand(r);
|
||||
vec2 new_uv = uv + rv * p;
|
||||
if (new_uv.x > 2. / width && new_uv.y > 2. / height && new_uv.x < 1. - 3. / width && new_uv.y < 1. - 3. / height) {
|
||||
c += texture2D(tex, new_uv);
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0 || use_base_pixel) {
|
||||
c += texture2D(tex, uv);
|
||||
count += 1;
|
||||
}
|
||||
|
||||
c.xyz *= brightness;
|
||||
cogl_color_out = c / count;
|
||||
}
|
||||
@ -0,0 +1,187 @@
|
||||
import GObject from 'gi://GObject';
|
||||
|
||||
import * as utils from '../conveniences/utils.js';
|
||||
const St = await utils.import_in_shell_only('gi://St');
|
||||
const Shell = await utils.import_in_shell_only('gi://Shell');
|
||||
const Clutter = await utils.import_in_shell_only('gi://Clutter');
|
||||
|
||||
const SHADER_FILENAME = 'monte_carlo_blur.glsl';
|
||||
const DEFAULT_PARAMS = {
|
||||
radius: 2., iterations: 5, brightness: .6,
|
||||
width: 0, height: 0, use_base_pixel: true
|
||||
};
|
||||
|
||||
|
||||
export const MonteCarloBlurEffect = utils.IS_IN_PREFERENCES ?
|
||||
{ default_params: DEFAULT_PARAMS } :
|
||||
new GObject.registerClass({
|
||||
GTypeName: "MonteCarloBlurEffect",
|
||||
Properties: {
|
||||
'radius': GObject.ParamSpec.double(
|
||||
`radius`,
|
||||
`Radius`,
|
||||
`Blur radius`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, 2000.0,
|
||||
2.0,
|
||||
),
|
||||
'iterations': GObject.ParamSpec.int(
|
||||
`iterations`,
|
||||
`Iterations`,
|
||||
`Blur iterations`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0, 64,
|
||||
5,
|
||||
),
|
||||
'brightness': GObject.ParamSpec.double(
|
||||
`brightness`,
|
||||
`Brightness`,
|
||||
`Blur brightness`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, 1.0,
|
||||
0.6,
|
||||
),
|
||||
'width': GObject.ParamSpec.double(
|
||||
`width`,
|
||||
`Width`,
|
||||
`Width`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, Number.MAX_SAFE_INTEGER,
|
||||
0.0,
|
||||
),
|
||||
'height': GObject.ParamSpec.double(
|
||||
`height`,
|
||||
`Height`,
|
||||
`Height`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, Number.MAX_SAFE_INTEGER,
|
||||
0.0,
|
||||
),
|
||||
'use_base_pixel': GObject.ParamSpec.boolean(
|
||||
`use_base_pixel`,
|
||||
`Use base pixel`,
|
||||
`Use base pixel`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
true,
|
||||
),
|
||||
}
|
||||
}, class MonteCarloBlurEffect extends Clutter.ShaderEffect {
|
||||
constructor(params) {
|
||||
super(params);
|
||||
|
||||
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);
|
||||
|
||||
const theme_context = St.ThemeContext.get_for_stage(global.stage);
|
||||
theme_context.connectObject(
|
||||
'notify::scale-factor',
|
||||
_ => this.set_uniform_value('radius',
|
||||
parseFloat(this._radius * theme_context.scale_factor - 1e-6)
|
||||
),
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
static get default_params() {
|
||||
return DEFAULT_PARAMS;
|
||||
}
|
||||
|
||||
get radius() {
|
||||
return this._radius;
|
||||
}
|
||||
|
||||
set radius(value) {
|
||||
if (this._radius !== value) {
|
||||
this._radius = value;
|
||||
|
||||
const scale_factor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
|
||||
this.set_uniform_value('radius', parseFloat(this._radius * scale_factor - 1e-6));
|
||||
this.set_enabled(this.radius > 0. && this.iterations > 0);
|
||||
}
|
||||
}
|
||||
|
||||
get iterations() {
|
||||
return this._iterations;
|
||||
}
|
||||
|
||||
set iterations(value) {
|
||||
if (this._iterations !== value) {
|
||||
this._iterations = value;
|
||||
|
||||
this.set_uniform_value('iterations', this._iterations);
|
||||
this.set_enabled(this.radius > 0. && this.iterations > 0);
|
||||
}
|
||||
}
|
||||
|
||||
get brightness() {
|
||||
return this._brightness;
|
||||
}
|
||||
|
||||
set brightness(value) {
|
||||
if (this._brightness !== value) {
|
||||
this._brightness = value;
|
||||
|
||||
this.set_uniform_value('brightness', parseFloat(this._brightness - 1e-6));
|
||||
}
|
||||
}
|
||||
|
||||
get width() {
|
||||
return this._width;
|
||||
}
|
||||
|
||||
set width(value) {
|
||||
if (this._width !== value) {
|
||||
this._width = value;
|
||||
|
||||
this.set_uniform_value('width', parseFloat(this._width + 3.0 - 1e-6));
|
||||
}
|
||||
}
|
||||
|
||||
get height() {
|
||||
return this._height;
|
||||
}
|
||||
|
||||
set height(value) {
|
||||
if (this._height !== value) {
|
||||
this._height = value;
|
||||
|
||||
this.set_uniform_value('height', parseFloat(this._height + 3.0 - 1e-6));
|
||||
}
|
||||
}
|
||||
|
||||
get use_base_pixel() {
|
||||
return this._use_base_pixel;
|
||||
}
|
||||
|
||||
set use_base_pixel(value) {
|
||||
if (this._use_base_pixel !== value) {
|
||||
this._use_base_pixel = value;
|
||||
|
||||
this.set_uniform_value('use_base_pixel', this._use_base_pixel ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
vfunc_set_actor(actor) {
|
||||
if (this._actor_connection_size_id) {
|
||||
let old_actor = this.get_actor();
|
||||
old_actor?.disconnect(this._actor_connection_size_id);
|
||||
}
|
||||
if (actor) {
|
||||
this.width = actor.width;
|
||||
this.height = actor.height;
|
||||
this._actor_connection_size_id = actor.connect('notify::size', _ => {
|
||||
this.width = actor.width;
|
||||
this.height = actor.height;
|
||||
});
|
||||
}
|
||||
else
|
||||
this._actor_connection_size_id = null;
|
||||
|
||||
super.vfunc_set_actor(actor);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,43 @@
|
||||
import GObject from 'gi://GObject';
|
||||
|
||||
import * as utils from '../conveniences/utils.js';
|
||||
const St = await utils.import_in_shell_only('gi://St');
|
||||
const Shell = await utils.import_in_shell_only('gi://Shell');
|
||||
|
||||
const DEFAULT_PARAMS = {
|
||||
unscaled_radius: 30, brightness: 0.6
|
||||
};
|
||||
|
||||
|
||||
export const NativeDynamicBlurEffect = utils.IS_IN_PREFERENCES ?
|
||||
{ default_params: DEFAULT_PARAMS } :
|
||||
new GObject.registerClass({
|
||||
GTypeName: "NativeDynamicBlurEffect"
|
||||
}, class NativeDynamicBlurEffect extends Shell.BlurEffect {
|
||||
constructor(params) {
|
||||
const { unscaled_radius, brightness, ...parent_params } = params;
|
||||
super({ ...parent_params, mode: Shell.BlurMode.BACKGROUND });
|
||||
|
||||
this._theme_context = St.ThemeContext.get_for_stage(global.stage);
|
||||
this._theme_context.connectObject(
|
||||
'notify::scale-factor',
|
||||
_ => this.radius = this.unscaled_radius * this._theme_context.scale_factor,
|
||||
this
|
||||
);
|
||||
|
||||
utils.setup_params(this, params);
|
||||
}
|
||||
|
||||
static get default_params() {
|
||||
return DEFAULT_PARAMS;
|
||||
}
|
||||
|
||||
get unscaled_radius() {
|
||||
return this._unscaled_radius;
|
||||
}
|
||||
|
||||
set unscaled_radius(value) {
|
||||
this._unscaled_radius = value;
|
||||
this.radius = value * this._theme_context.scale_factor;
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,43 @@
|
||||
import GObject from 'gi://GObject';
|
||||
|
||||
import * as utils from '../conveniences/utils.js';
|
||||
const St = await utils.import_in_shell_only('gi://St');
|
||||
const Shell = await utils.import_in_shell_only('gi://Shell');
|
||||
|
||||
const DEFAULT_PARAMS = {
|
||||
unscaled_radius: 30, brightness: 0.6
|
||||
};
|
||||
|
||||
|
||||
export const NativeStaticBlurEffect = utils.IS_IN_PREFERENCES ?
|
||||
{ default_params: DEFAULT_PARAMS } :
|
||||
new GObject.registerClass({
|
||||
GTypeName: "NativeStaticBlurEffect"
|
||||
}, class NativeStaticBlurEffect extends Shell.BlurEffect {
|
||||
constructor(params) {
|
||||
const { unscaled_radius, brightness, ...parent_params } = params;
|
||||
super({ ...parent_params, mode: Shell.BlurMode.ACTOR });
|
||||
|
||||
this._theme_context = St.ThemeContext.get_for_stage(global.stage);
|
||||
this._theme_context.connectObject(
|
||||
'notify::scale-factor',
|
||||
_ => this.radius = this.unscaled_radius * this._theme_context.scale_factor,
|
||||
this
|
||||
);
|
||||
|
||||
utils.setup_params(this, params);
|
||||
}
|
||||
|
||||
static get default_params() {
|
||||
return DEFAULT_PARAMS;
|
||||
}
|
||||
|
||||
get unscaled_radius() {
|
||||
return this._unscaled_radius;
|
||||
}
|
||||
|
||||
set unscaled_radius(value) {
|
||||
this._unscaled_radius = value;
|
||||
this.radius = value * this._theme_context.scale_factor;
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,76 @@
|
||||
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 = 'noise.glsl';
|
||||
const DEFAULT_PARAMS = {
|
||||
noise: 0.4, lightness: 0.4
|
||||
};
|
||||
|
||||
|
||||
export const NoiseEffect = utils.IS_IN_PREFERENCES ?
|
||||
{ default_params: DEFAULT_PARAMS } :
|
||||
new GObject.registerClass({
|
||||
GTypeName: "NoiseEffect",
|
||||
Properties: {
|
||||
'noise': GObject.ParamSpec.double(
|
||||
`noise`,
|
||||
`Noise`,
|
||||
`Amount of noise integrated with the image`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, 1.0,
|
||||
0.4,
|
||||
),
|
||||
'lightness': GObject.ParamSpec.double(
|
||||
`lightness`,
|
||||
`Lightness`,
|
||||
`Lightness of the grey used for the noise`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, 2.0,
|
||||
0.4,
|
||||
),
|
||||
}
|
||||
}, class NoiseEffect extends Clutter.ShaderEffect {
|
||||
constructor(params) {
|
||||
super(params);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static get default_params() {
|
||||
return DEFAULT_PARAMS;
|
||||
}
|
||||
|
||||
get noise() {
|
||||
return this._noise;
|
||||
}
|
||||
|
||||
set noise(value) {
|
||||
if (this._noise !== value) {
|
||||
this._noise = value;
|
||||
|
||||
this.set_uniform_value('noise', parseFloat(this._noise - 1e-6));
|
||||
this.set_enabled(this.noise > 0. && this.lightness != 1);
|
||||
}
|
||||
}
|
||||
|
||||
get lightness() {
|
||||
return this._lightness;
|
||||
}
|
||||
|
||||
set lightness(value) {
|
||||
if (this._lightness !== value) {
|
||||
this._lightness = value;
|
||||
|
||||
this.set_uniform_value('lightness', parseFloat(this._lightness - 1e-6));
|
||||
this.set_enabled(this.noise > 0. && this.lightness != 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -1,109 +0,0 @@
|
||||
import GLib from 'gi://GLib';
|
||||
import GObject from 'gi://GObject';
|
||||
import Clutter from 'gi://Clutter';
|
||||
import Shell from 'gi://Shell';
|
||||
|
||||
|
||||
const SHADER_PATH = GLib.filename_from_uri(GLib.uri_resolve_relative(import.meta.url, 'noise_effect.glsl', GLib.UriFlags.NONE))[0];
|
||||
|
||||
const get_shader_source = _ => {
|
||||
try {
|
||||
return Shell.get_file_contents_utf8_sync(SHADER_PATH);
|
||||
} catch (e) {
|
||||
console.warn(`[Blur my Shell] error loading shader from ${SHADER_PATH}: ${e}`);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const NoiseEffect = new GObject.registerClass({
|
||||
GTypeName: "NoiseEffect",
|
||||
Properties: {
|
||||
'noise': GObject.ParamSpec.double(
|
||||
`noise`,
|
||||
`Noise`,
|
||||
`Amount of noise integrated with the image`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, 1.0,
|
||||
0.4,
|
||||
),
|
||||
'lightness': GObject.ParamSpec.double(
|
||||
`lightness`,
|
||||
`Lightness`,
|
||||
`Lightness of the grey used for the noise`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, 2.0,
|
||||
0.4,
|
||||
),
|
||||
}
|
||||
}, class NoiseShader extends Clutter.ShaderEffect {
|
||||
constructor(params, settings) {
|
||||
super(params);
|
||||
|
||||
this._noise = null;
|
||||
this._lightness = null;
|
||||
|
||||
this._static = true;
|
||||
this._settings = settings;
|
||||
|
||||
if (params.noise)
|
||||
this.noise = params.noise;
|
||||
if (params.lightness)
|
||||
this.lightness = params.lightness;
|
||||
|
||||
// set shader source
|
||||
this._source = get_shader_source();
|
||||
if (this._source)
|
||||
this.set_shader_source(this._source);
|
||||
|
||||
this.update_enabled();
|
||||
}
|
||||
|
||||
get noise() {
|
||||
return this._noise;
|
||||
}
|
||||
|
||||
set noise(value) {
|
||||
if (this._noise !== value) {
|
||||
this._noise = value;
|
||||
|
||||
this.set_uniform_value('noise', parseFloat(this._noise - 1e-6));
|
||||
}
|
||||
this.update_enabled();
|
||||
}
|
||||
|
||||
get lightness() {
|
||||
return this._lightness;
|
||||
}
|
||||
|
||||
set lightness(value) {
|
||||
if (this._lightness !== value) {
|
||||
this._lightness = value;
|
||||
|
||||
this.set_uniform_value('lightness', parseFloat(this._lightness - 1e-6));
|
||||
}
|
||||
}
|
||||
|
||||
update_enabled() {
|
||||
// don't anything if this._settings is undefined (when calling super)
|
||||
if (this._settings === undefined)
|
||||
return;
|
||||
|
||||
this.set_enabled(
|
||||
this.noise > 0 &&
|
||||
this._settings.COLOR_AND_NOISE &&
|
||||
this._static
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
vfunc_paint_target(paint_node = null, paint_context = null) {
|
||||
this.set_uniform_value("tex", 0);
|
||||
|
||||
if (paint_node && paint_context)
|
||||
super.vfunc_paint_target(paint_node, paint_context);
|
||||
else if (paint_node)
|
||||
super.vfunc_paint_target(paint_node);
|
||||
else
|
||||
super.vfunc_paint_target();
|
||||
}
|
||||
});
|
||||
@ -1,89 +0,0 @@
|
||||
import GObject from 'gi://GObject';
|
||||
import Clutter from 'gi://Clutter';
|
||||
|
||||
|
||||
export const PaintSignals = class PaintSignals {
|
||||
constructor(connections) {
|
||||
this.buffer = [];
|
||||
this.connections = connections;
|
||||
}
|
||||
|
||||
connect(actor, blur_effect) {
|
||||
let paint_effect = new EmitPaintSignal();
|
||||
let infos = {
|
||||
actor: actor,
|
||||
paint_effect: paint_effect
|
||||
};
|
||||
let counter = 0;
|
||||
|
||||
actor.add_effect(paint_effect);
|
||||
this.connections.connect(paint_effect, 'update-blur', () => {
|
||||
try {
|
||||
// checking if blur_effect.queue_repaint() has been recently called
|
||||
if (counter === 0) {
|
||||
counter = 2;
|
||||
blur_effect.queue_repaint();
|
||||
}
|
||||
else counter--;
|
||||
} catch (e) { }
|
||||
});
|
||||
|
||||
// remove the actor from buffer when it is destroyed
|
||||
if (
|
||||
actor.connect &&
|
||||
(
|
||||
!(actor instanceof GObject.Object) ||
|
||||
GObject.signal_lookup('destroy', actor)
|
||||
)
|
||||
)
|
||||
this.connections.connect(actor, 'destroy', () => {
|
||||
this.buffer.forEach(infos => {
|
||||
if (infos.actor === actor) {
|
||||
// remove from buffer
|
||||
let index = this.buffer.indexOf(infos);
|
||||
this.buffer.splice(index, 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.buffer.push(infos);
|
||||
}
|
||||
|
||||
disconnect_all_for_actor(actor) {
|
||||
this.buffer.forEach(infos => {
|
||||
if (infos.actor === actor) {
|
||||
this.connections.disconnect_all_for(infos.paint_effect);
|
||||
infos.actor.remove_effect(infos.paint_effect);
|
||||
|
||||
// remove from buffer
|
||||
let index = this.buffer.indexOf(infos);
|
||||
this.buffer.splice(index, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
disconnect_all() {
|
||||
this.buffer.forEach(infos => {
|
||||
this.connections.disconnect_all_for(infos.paint_effect);
|
||||
infos.actor.remove_effect(infos.paint_effect);
|
||||
});
|
||||
|
||||
this.buffer = [];
|
||||
}
|
||||
};
|
||||
|
||||
export const EmitPaintSignal = GObject.registerClass({
|
||||
GTypeName: 'EmitPaintSignal',
|
||||
Signals: {
|
||||
'update-blur': {
|
||||
param_types: []
|
||||
},
|
||||
}
|
||||
},
|
||||
class EmitPaintSignal extends Clutter.Effect {
|
||||
vfunc_paint(node, paint_context, paint_flags) {
|
||||
this.emit("update-blur");
|
||||
super.vfunc_paint(node, paint_context, paint_flags);
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -0,0 +1,88 @@
|
||||
import GObject from 'gi://GObject';
|
||||
|
||||
import * as utils from '../conveniences/utils.js';
|
||||
const Clutter = await utils.import_in_shell_only('gi://Clutter');
|
||||
|
||||
import { UpscaleEffect } from './upscale.js';
|
||||
import { DownscaleEffect } from './downscale.js';
|
||||
|
||||
const DEFAULT_PARAMS = {
|
||||
factor: 8, downsampling_mode: 0
|
||||
};
|
||||
|
||||
|
||||
export const PixelizeEffect = utils.IS_IN_PREFERENCES ?
|
||||
{ default_params: DEFAULT_PARAMS } :
|
||||
new GObject.registerClass({
|
||||
GTypeName: "PixelizeEffect",
|
||||
Properties: {
|
||||
'factor': GObject.ParamSpec.int(
|
||||
`factor`,
|
||||
`Factor`,
|
||||
`Factor`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0, 64,
|
||||
8,
|
||||
),
|
||||
'downsampling_mode': GObject.ParamSpec.int(
|
||||
`downsampling_mode`,
|
||||
`Downsampling mode`,
|
||||
`Downsampling mode`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0, 2,
|
||||
0,
|
||||
)
|
||||
}
|
||||
}, class PixelizeEffect extends Clutter.Effect {
|
||||
constructor(params) {
|
||||
super();
|
||||
|
||||
this.upscale_effect = new UpscaleEffect({});
|
||||
this.downscale_effect = new DownscaleEffect({});
|
||||
|
||||
utils.setup_params(this, params);
|
||||
}
|
||||
|
||||
static get default_params() {
|
||||
return DEFAULT_PARAMS;
|
||||
}
|
||||
|
||||
get factor() {
|
||||
// should be the same as `this.downscale_effect.divider`
|
||||
return this.upscale_effect.factor;
|
||||
}
|
||||
|
||||
set factor(value) {
|
||||
this.upscale_effect.factor = value;
|
||||
this.downscale_effect.divider = value;
|
||||
}
|
||||
|
||||
get downsampling_mode() {
|
||||
return this.downscale_effect.downsampling_mode;
|
||||
}
|
||||
|
||||
set downsampling_mode(value) {
|
||||
this.downscale_effect.downsampling_mode = value;
|
||||
}
|
||||
|
||||
vfunc_set_actor(actor) {
|
||||
// deattach effects from old actor
|
||||
this.upscale_effect?.actor?.remove_effect(this.upscale_effect);
|
||||
this.downscale_effect?.actor?.remove_effect(this.downscale_effect);
|
||||
// attach effects to new actor
|
||||
if (actor) {
|
||||
if (this.upscale_effect)
|
||||
actor.add_effect(this.upscale_effect);
|
||||
if (this.downscale_effect)
|
||||
actor.add_effect(this.downscale_effect);
|
||||
}
|
||||
|
||||
super.vfunc_set_actor(actor);
|
||||
}
|
||||
|
||||
vfunc_set_enabled(is_enabled) {
|
||||
this.upscale_effect?.set_enabled(is_enabled);
|
||||
this.downscale_effect?.set_enabled(is_enabled);
|
||||
super.vfunc_set_enabled(is_enabled);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,18 @@
|
||||
uniform sampler2D tex;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
vec2 uv = cogl_tex_coord_in[0].xy;
|
||||
vec4 rgba = texture2D(tex, uv);
|
||||
vec4 hsla = vec4(rgb_to_hsl(rgba.xyz), rgba.w);
|
||||
cogl_color_out = hsla;
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
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 = 'rgb_to_hsl.glsl';
|
||||
const DEFAULT_PARAMS = {};
|
||||
|
||||
|
||||
export const RgbToHslEffect = utils.IS_IN_PREFERENCES ?
|
||||
{ default_params: DEFAULT_PARAMS } :
|
||||
new GObject.registerClass({
|
||||
GTypeName: "RgbToHslEffect",
|
||||
Properties: {}
|
||||
}, class RgbToHslEffect extends Clutter.ShaderEffect {
|
||||
constructor(params) {
|
||||
super(params);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static get default_params() {
|
||||
return DEFAULT_PARAMS;
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,57 @@
|
||||
uniform sampler2D tex;
|
||||
uniform int factor;
|
||||
uniform float width;
|
||||
uniform float height;
|
||||
|
||||
#define CORRECTION 2.25
|
||||
#define SIZE_ADDITION 3
|
||||
|
||||
vec4 get_texture_at_position(vec2 position) {
|
||||
vec2 raw_position = position + vec2(CORRECTION, CORRECTION);
|
||||
vec2 raw_uv = raw_position / vec2(width + SIZE_ADDITION, height + SIZE_ADDITION);
|
||||
|
||||
return texture2D(tex, raw_uv);
|
||||
}
|
||||
|
||||
ivec2 get_corrected_position() {
|
||||
vec2 raw_uv = cogl_tex_coord0_in.st;
|
||||
vec2 raw_position = raw_uv * vec2(width + SIZE_ADDITION, height + SIZE_ADDITION);
|
||||
return ivec2(raw_position - vec2(CORRECTION, CORRECTION));
|
||||
}
|
||||
|
||||
void main() {
|
||||
ivec2 corrected_position = get_corrected_position();
|
||||
|
||||
vec2 adjusted_position = corrected_position / factor;
|
||||
|
||||
cogl_color_out = get_texture_at_position(adjusted_position);
|
||||
|
||||
// round
|
||||
if (distance(corrected_position, (floor(adjusted_position) + 0.5) * factor) < factor / 2.5) {
|
||||
//cogl_color_out = get_texture_at_position(adjusted_position);
|
||||
} else {
|
||||
//cogl_color_out = vec4(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
// square
|
||||
if (mod(corrected_position.x, factor) >= 2 && mod(corrected_position.y, factor) >= 2) {
|
||||
//cogl_color_out = get_texture_at_position(adjusted_position);
|
||||
} else {
|
||||
//cogl_color_out = vec4(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
// local mix
|
||||
vec4 color = vec4(0);
|
||||
int count = 0;
|
||||
for (int i = -1; i <= 1; i++) {
|
||||
for (int j = -1; j <= 1; j++) {
|
||||
vec2 lookup_position = adjusted_position + vec2(i, j);
|
||||
if (all(greaterThanEqual(lookup_position, vec2(0, 0))) &&
|
||||
all(lessThan(lookup_position, vec2(width, height) / factor))) {
|
||||
color += get_texture_at_position(lookup_position);
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
//cogl_color_out = color / count;
|
||||
}
|
||||
@ -0,0 +1,120 @@
|
||||
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 = 'upscale.glsl';
|
||||
const DEFAULT_PARAMS = {
|
||||
factor: 8, width: 0, height: 0
|
||||
};
|
||||
|
||||
|
||||
export const UpscaleEffect = utils.IS_IN_PREFERENCES ?
|
||||
{ default_params: DEFAULT_PARAMS } :
|
||||
new GObject.registerClass({
|
||||
GTypeName: "UpscaleEffect",
|
||||
Properties: {
|
||||
'factor': GObject.ParamSpec.int(
|
||||
`factor`,
|
||||
`Factor`,
|
||||
`Factor`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0, 64,
|
||||
8,
|
||||
),
|
||||
'width': GObject.ParamSpec.double(
|
||||
`width`,
|
||||
`Width`,
|
||||
`Width`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, Number.MAX_SAFE_INTEGER,
|
||||
0.0,
|
||||
),
|
||||
'height': GObject.ParamSpec.double(
|
||||
`height`,
|
||||
`Height`,
|
||||
`Height`,
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0, Number.MAX_SAFE_INTEGER,
|
||||
0.0,
|
||||
)
|
||||
}
|
||||
}, class UpscaleEffect extends Clutter.ShaderEffect {
|
||||
constructor(params) {
|
||||
super(params);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static get default_params() {
|
||||
return DEFAULT_PARAMS;
|
||||
}
|
||||
|
||||
get factor() {
|
||||
return this._factor;
|
||||
}
|
||||
|
||||
set factor(value) {
|
||||
if (this._factor !== value) {
|
||||
this._factor = value;
|
||||
|
||||
this.set_uniform_value('factor', this._factor);
|
||||
}
|
||||
}
|
||||
|
||||
get width() {
|
||||
return this._width;
|
||||
}
|
||||
|
||||
set width(value) {
|
||||
if (this._width !== value) {
|
||||
this._width = value;
|
||||
|
||||
this.set_uniform_value('width', parseFloat(this._width - 1e-6));
|
||||
}
|
||||
}
|
||||
|
||||
get height() {
|
||||
return this._height;
|
||||
}
|
||||
|
||||
set height(value) {
|
||||
if (this._height !== value) {
|
||||
this._height = value;
|
||||
|
||||
this.set_uniform_value('height', parseFloat(this._height - 1e-6));
|
||||
}
|
||||
}
|
||||
|
||||
vfunc_set_actor(actor) {
|
||||
if (this._actor_connection_size_id) {
|
||||
let old_actor = this.get_actor();
|
||||
old_actor?.disconnect(this._actor_connection_size_id);
|
||||
}
|
||||
if (actor) {
|
||||
this.width = actor.width;
|
||||
this.height = actor.height;
|
||||
this._actor_connection_size_id = actor.connect('notify::size', _ => {
|
||||
this.width = actor.width;
|
||||
this.height = actor.height;
|
||||
});
|
||||
}
|
||||
else
|
||||
this._actor_connection_size_id = null;
|
||||
|
||||
super.vfunc_set_actor(actor);
|
||||
}
|
||||
|
||||
vfunc_paint_target(paint_node, paint_context) {
|
||||
// force setting nearest-neighbour texture filtering
|
||||
this.get_pipeline().set_layer_filters(0, 9728, 9728);
|
||||
|
||||
super.vfunc_paint_target(paint_node, paint_context);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user