Skip to content
nick87720z edited this page Dec 14, 2022 · 23 revisions

This page lists window shaders created by users of picom. You can put your own shaders here.

For information on how to use these shaders, please consult the --glx-fshader-win option in the man page. Note currently this feature is not implemented in the experimental backends yet.

Grayscale filter

Author: @yshui

Description: This shader turns all of your windows black-and-white.

Screenshot:

uniform sampler2D tex;
uniform float opacity;
void main() {
    vec4 color = texture2D(tex, gl_TexCoord[0].xy);
    gl_FragColor = vec4(
        vec3(0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b) * opacity,
        color.a * opacity);
}

Colorscheme filter

Author: @KiranWells

Description: This shader restricts all windows to a certain colorscheme, by default Nord.

Screenshot:

shader-nord.jpg

// The Nord colorscheme is used by default, 
// but you can make your own by changing `colors`

uniform float opacity;
uniform float time;
uniform bool invert_color;
uniform sampler2D tex;

float sin_rand() {
  return sin(gl_FragCoord.x + cos(gl_FragCoord.y));
}

float random(float seedChange) {
  vec2 seed = gl_FragCoord.xy + sin(seedChange);
  return fract(dot(vec2(sin(mod(seed.x / cos(seed.y), 5.0) * 10000.0)), vec2(1.1, 12.2)));
}

void main() {
  vec4 c = texture2D(tex, gl_TexCoord[0].xy);
  vec4 d = c;
  vec3 colors[16];
    colors[0 ] = vec3(0.18 ,0.204,0.251);
    colors[1 ] = vec3(0.231,0.259,0.322);
    colors[2 ] = vec3(0.263,0.298,0.369);
    colors[3 ] = vec3(0.298,0.337,0.416);
    colors[4 ] = vec3(0.847,0.871,0.914);
    colors[5 ] = vec3(0.898,0.914,0.941);
    colors[6 ] = vec3(0.925,0.937,0.957);
    colors[7 ] = vec3(0.561,0.737,0.733);
    colors[8 ] = vec3(0.533,0.753,0.816);
    colors[9 ] = vec3(0.506,0.631,0.757);
    colors[10] = vec3(0.369,0.506,0.675);
    colors[11] = vec3(0.749,0.38 ,0.416);
    colors[12] = vec3(0.816,0.529,0.439);
    colors[13] = vec3(0.922,0.796,0.545);
    colors[14] = vec3(0.639,0.745,0.549);
    colors[15] = vec3(0.706,0.557,0.678);
 
  float mindist = 100.0;
  int minind = 0;
  float mindist2 = 100.0;
  int minind2 = 0;
  for (int i = 0; i < 16; i++) {
    float dist = length(c.xyz - colors[i]);
    if (dist < mindist) {
      mindist2 = mindist;
      mindist = dist;
      minind2 = minind;
      minind = i;
    }
  }
  float ratio = mindist / (mindist + mindist2);
  float r = random(1.0) * 0.4 + 0.25;
  if (r > ratio)
    c.xyz = colors[minind];
  else 
    c.xyz = colors[minind2];

  c.xyz = mix(mix(colors[minind], colors[minind2], ratio), c.xyz, 0.5);
  
  if (invert_color)
    c = vec4(vec3(c.a, c.a, c.a) - vec3(c), c.a);
  c *= opacity;
  gl_FragColor = vec4(c);
}

Swizzle rainbow

Author: @FranchuFranchu

Description: This shader slowly "swizzles" the colors of your windows. Swizzling means, for example, changing all the green colors to red, all the red colors to blue, and all the blue colors to green. The result is the colors of your screen changing like a rainbow.

Screenshot:

screenshot.png

#version 130
#extension GL_ARB_shading_language_420pack: enable

#define CYCLE 5000 // The amount of miliseconds it takes to do a full "loop" around all the colors.

uniform float opacity;
uniform bool invert_color;
uniform sampler2D tex;
uniform float time;

float get_decimal_part(float f) {
	return f - int(f);
}

float snap0(float f) {
	return (f < 0) ? 0 : f;
}

void main() {
	vec4 c = texture2D(tex, gl_TexCoord[0].xy);
	float f = get_decimal_part(time / CYCLE);

	gl_FragColor.a = 1;

	float p[3] = {
		snap0(0.33 - abs(f - 0.33)) * 4,
		snap0(0.33 - abs(f - 0.66)) * 4,
		snap0(0.33 - abs(f - 1.00)) * 4 + snap0(0.33 - abs(f - 0.0)) * 4
	};
	gl_FragColor.r = p[0] * c.r + p[1] * c.g + p[2] * c.b;
	gl_FragColor.g = p[2] * c.r + p[0] * c.g + p[1] * c.b;
	gl_FragColor.b = p[1] * c.r + p[2] * c.g + p[0] * c.b;
}

Color-preserving Invert

Author: @KiranWells

Description: This shader preserves the colors of an image when inverting the value. It allows the invert flag to be used without causing an unsightly neon mess. This helps preserve the look of pictures and things while keeping the high contrast look. Note: this shader has no effect unless the invert_color flag is set to true. To permanently enable it, replace the if statement to always be true.

Screenshot:

invert-shader.jpg

// Changes the behavior of inverting color to preserve the hue
// In other words, the invert flag will now only invert the 
// lightness value. This helps preserve the look of pictures and
// things while keeping the high contrast look.

uniform float opacity;
uniform bool invert_color;
uniform sampler2D tex;

#define pi 3.14159265358979323846264

// adapted from https://stackoverflow.com/questions/9234724/how-to-change-hue-of-a-texture-with-glsl
vec3 hue_rotate(in vec3 c, in float hueAdjust) {
  const vec4  kRGBToYPrime = vec4 (0.299, 0.587, 0.114, 0.0);
  const vec4  kRGBToI     = vec4 (0.596, -0.275, -0.321, 0.0);
  const vec4  kRGBToQ     = vec4 (0.212, -0.523, 0.311, 0.0);

  const vec4  kYIQToR   = vec4 (1.0, 0.956, 0.621, 0.0);
  const vec4  kYIQToG   = vec4 (1.0, -0.272, -0.647, 0.0);
  const vec4  kYIQToB   = vec4 (1.0, -1.107, 1.704, 0.0);

  vec4    color   = vec4(c, 1.0);

  // Convert to YIQ
  float   YPrime  = dot (color, kRGBToYPrime);
  float   I      = dot (color, kRGBToI);
  float   Q      = dot (color, kRGBToQ);

  // Calculate the hue and chroma
  float   hue     = atan (Q, I);
  float   chroma  = sqrt (I * I + Q * Q);

  // Make the adjustments
  hue += hueAdjust * 2. * pi;

  // Convert back to YIQ
  Q = chroma * sin (hue);
  I = chroma * cos (hue);

  // Convert back to RGB
  vec4    yIQ   = vec4 (YPrime, I, Q, 0.0);
  color.r = dot (yIQ, kYIQToR);
  color.g = dot (yIQ, kYIQToG);
  color.b = dot (yIQ, kYIQToB);

  return color.rgb;
}

void main() {
	vec4 c = texture2D(tex, gl_TexCoord[0].xy);
	
	if (invert_color) {
		c.xyz = 1.0 - c.xyz;
		c.xyz = hue_rotate(c.xyz, 0.5);
	}
	c *= opacity;
  gl_FragColor = vec4(c);
}

Fullscreen subpixel antialiasing

Author: @nick87720z

Description: This shader allowes to get fullscreen subpixel or greyscale antialiasing in X11 window system with XRandr assistance. Written in honor of expiration of patents, covering subpixel graphics layout. This can make LCD display look like CRT / analogue display.

Principle:

  • XRandr sets virtual display resolutions 3x higher than physical (for popular rgb layouts, supported by freetype).
  • Picom applies shader, which filters content so, that Xrandr can just pick pixels using nearest-neighbor scaling mode.

Be advised: it's very power hungry, which can overheat system, turn PC into toster, scramble cocroaches... (you were warned).

Screenshot:
(needs update, doesn't reflect gamma correction effect)

Note: This screenshot is made for horizontal RGB subpixel layout, supported by fontconfig. For bgr or vertical variants try to rotate to 90, 180 or 270 deg.

subpixel-screenshot-4-in-1 zopfli

Code: downscale-filter.glsl
#version 330
/**
 * Goal: Fullscreen subpixel antialiasing filter.
 *
 * Motivation:
 * - Natural, less pixelated look.
 *   See https://entropymine.com/imageworsener/subpixel (note: there's image, useful to calibrate subpixel shift parameter).
 * - It's written while celebrating the expiration of patents, covering subpixel graphics layout.
 *
 * Principle:
 * - XRandr sets 3x scaled virtual display resolutions 3x higher than physical (for popular rgb layouts, supported by freetype).
 * - Picom shader performs all the downscale filtering (interpolation, channels shift, sharpening) against content with multiplied resolution.
 *   - Sharpening amount is kept just enough to get same crispness as for freetype-rendered text.
 * - After it's done, necessary pixels are simply picked by Xrandr with nearest neighbor downscale method.
 *
 * Downsides:
 * Very power hungry, so good graphics card is required for relatively smooth experience (good if not gaming grade).
 * Besides 9x more Mpix, there's also some ~2x overhead (could be more), yet there's possible frequency throtling on overheat.
 *
 * Setup:
 * - Other subpixel rendering methods (e.g. font subpixel rendering) don't combine with this.
 * - Mouse pointer bypasses picom, so you may need prefiltered mouse theme.
 * - WARNING: This shader is meaned to be applied to screen instead of windows.
 *   When used per-window, this may need more job than one screen; using 'wx_3fold' makes empty content for windows, not aligned to 3x3 pixel block center.
 *
 * Example ~/.xprofile:
 * ############################
 *		export GDK_SCALE=3
 *		export GDK_DPI_SCALE=0.33333
 *		export ELM_SCALE=3
 *		export QT_AUTO_SCREEN_SCALE_FACTOR=0
 *		export QT_SCREEN_SCALE_FACTORS=3
 *
 *      # Size needs to be configured early, before desktop loading, or it could behave incorrectly.
 *		xrandr --output LVDS1 --scale 1x1 --filter nearest 
 *		xrandr --output LVDS1 --scale 3x3 --filter nearest
 * ############################
 */

/*********** Configuration choices ************/

#define wx_3fold 0 /** Flag to skip positions to be ignored by following nearest filter, greatly reducing power waste.
                    ** Only for 3-fold window position (best use is for screen post-processing). */
#define FUNC 1  /** Filter function choice.
                 ** 0 - 3x3 averaging: no stages (default, intended for first run test),
                 ** 1 - 3x3 averaging + subpixel: 0 to 2 stages. */
#define STAGE 0 /** Stage number to run (0 - single stage version). */
#define GAMMA 2.0 /** Use custom gamma correction instead of builtin square.
                   ** NOTE: Use proper gamma calibration tool to find right value, don't judge by color fringes intensity. */

#if FUNC == 1
	#define LCD_RGB     /** Subpixel order choice:       LCD_RGB,  LCD_BGR. */
	#define LCD_HORZ    /** Subpixel layout orientation: LCD_HORZ, LCD_VERT. */
	#define shift (100) /** Subpixel shift in percents from full (0..1/3 pixel width for linear RGB stripes). */
	#define RGB_CONTRAST 1.0
	/** Antiblur level. Negates extra blur after box is applied to existing 3px or wider areas.
	 ** Recomended default is 1.0. Higher values might have clear support shadows overweight. */
#endif

/*********** Implementations ***********************/

/** Development note:
 ** When 'wx_3fold' is enabled, filtering in multiple per-axis stages doesn't enhance performance for small kernels,
 ** because 1st stage can skip only 1/3 points instead of 1/9.
 **
 ** Expecting full-screen post-processing support in picom. */

vec4 default_post_processing( vec4 c);

uniform vec2 texcoord;
uniform sampler2D tex;

#if wx_3fold
	const vec4 skip_color = vec4( 0.0, 0.0, 0.0, 1.0);
#endif

#if defined GAMMA
	const vec4 pow_to_lin   = vec4( 2.2);
	const vec4 pow_from_lin = vec4( 1.0 / 2.2);
	#define to_lin( c)   c=pow( c, pow_to_lin )
	#define from_lin( c) c=pow( c, pow_from_lin )
#else
	#define to_lin( c)   c*=( c)
	#define from_lin( c) c=sqrt( c)
#endif

#if FUNC == 0
/***************************** Greyscale 3x3 box (test) *****************************/

vec4 window_shader()
{
#if wx_3fold
	if (int( texcoord.x) % 3 != 1)
		return skip_color;
	if (int( texcoord.y) % 3 != 1)
		return skip_color;
#endif
	vec4 accum = vec4( 0), tmp;
	ivec2 p = ivec2( texcoord);
	p.y--;
	int x = int( texcoord.x) - 1;
	for (int i=0; i<3; i++, p.y++)
	{
		p.x = x;
		for (int j=0; j<3; j++, p.x++)
		{
			tmp = texelFetch( tex, p, 0);
			to_lin( tmp);
			accum += tmp;
		}
	}
	accum /= 9;
	from_lin( accum);
	return accum;
}

#endif
/********** Subpixel: linear RGB stripe format ********************************/
#if FUNC == 1

#if defined LCD_HORZ
	#define sub_x x
	#define sub_y y
#elif defined LCD_VERT
	#define sub_x y
	#define sub_y x
#endif

#if defined LCD_RGB
	#define ch_1st  r
	#define ch_last b
#elif defined LCD_BGR
	#define ch_1st  b
	#define ch_last r
#endif

#if defined shift
	#if (shift == 100)
		#undef shift
	#else
		float _shift = float( shift) / 100;
	#endif
#endif

#define RGB_SOFT int( RGB_CONTRAST == 0.0 ? 9000.0 : (27.0 / RGB_CONTRAST))

#if defined shift
	#define chan_shift( a, l)              \
	for (int j=1; j <= l/2; j++) {          \
		a[ l-1-j].ch_1st  = (1.0 - _shift) * a[ l-1-j].ch_1st  + _shift * a[ l-2-j].ch_1st; \
		a[ j].ch_last     = (1.0 - _shift) * a[ j].ch_last + _shift * a[ 1+j].ch_last;      \
	}                                        \
	to_lin( a[ l/2 ]);                       \
	for (int j=l/2+1; j < l-1; j++) {        \
		a[ l-1-j].ch_1st  = (1.0 - _shift) * a[ l-1-j].ch_1st  + _shift * a[ l-2-j].ch_1st; \
		a[ j].ch_last     = (1.0 - _shift) * a[ j].ch_last + _shift * a[ 1+j].ch_last;      \
		to_lin( a[ j]), to_lin( a[ l-1-j]);  \
	}
#else
	#define chan_shift( a, l)              \
	for (int j=1; j <= l/2; j++) {          \
		a[ l-1-j].ch_1st = a[ l-2-j].ch_1st; \
		a[ j].ch_last    = a[ 1+j].ch_last;  \
	}                                        \
	to_lin( a[ l/2 ]);                       \
	for (int j=l/2+1; j < l-1; j++) {        \
		a[ l-1-j].ch_1st = a[ l-2-j].ch_1st; \
		a[ j].ch_last    = a[ 1+j].ch_last;  \
		to_lin( a[ j]), to_lin( a[ l-1-j]);  \
	}
#endif

#define prepare_subpix_line( a, l, c) \
	c.sub_x = int( texcoord.sub_x) - int( l / 2); \
	for (int j=0; j < l; j++, c.sub_x++) \
	{                                     \
		a[ j] = texelFetch( tex, c, 0); /* Reading between stripes */ \
	}                     \
	chan_shift( a, l)

#define return_foreach_sub_line( l, accum_expr, accum_div) \
{                      \
	ivec2 p;            \
	p.sub_y = int( texcoord.sub_y) - 1; \
	for (int i=0; i < 3; i++, p.sub_y++) \
	{                                    \
		vec4 c[ l];                      \
		prepare_subpix_line( c, l, p);   \
		accum_expr;                       \
	}                                      \
	accum_div;                             \
}

#if STAGE == 0
/**************** One-stage implementation (worse performance) *******************/

vec4 window_shader()
{
#if wx_3fold
	if (int( texcoord.x) % 3 != 1)
		return skip_color;
	if (int( texcoord.y) % 3 != 1)
		return skip_color;
#endif
	vec4 accum[ 3] = vec4[ 3]( vec4(0), vec4(0), vec4(0) );
	return_foreach_sub_line(
		11,
		( accum[ 0] += c[1] + c[2] + c[3],
		  accum[ 1] += c[4] + c[5] + c[6],
		  accum[ 2] += c[7] + c[8] + c[9] ),
		( accum[ 0] /= 9, from_lin( accum[ 0]),
		  accum[ 1] /= 9, from_lin( accum[ 1]),
		  accum[ 2] /= 9, from_lin( accum[ 2]) )
	);
	return (RGB_SOFT * accum[ 1] - accum[ 0] - accum[ 2]) / (RGB_SOFT - 2);
}

#elif STAGE == 1
/**************** Stage 1 *******************/

vec4 window_shader()
{
#if wx_3fold
	if (int( texcoord.x) % 3 != 1)
		return skip_color;
	if (int( texcoord.y) % 3 != 1)
		return skip_color;
#endif
	vec4 accum = vec4(0);
	return_foreach_sub_line(
		5,
		accum += c[1] + c[2] + c[3],
		accum /= 9
	);
	from_lin( accum);
	return accum;
}

#elif STAGE == 2
/**************** Stage 2 *******************/
// NOTE: No gamma correction (contrast must be in perception scale)

vec4 window_shader()
{
#if wx_3fold
	if (int( texcoord.x) % 3 != 1)
		return skip_color;
	if (int( texcoord.y) % 3 != 1)
		return skip_color;
#endif
	vec4 c[3];
	ivec2 p;
	p.sub_y = int( texcoord.sub_y);
	p.sub_x = int( texcoord.sub_x) - 3;
	c[0] = texelFetch( tex, p, 0); p.sub_x += 3
	c[1] = texelFetch( tex, p, 0); p.sub_x += 3
	c[2] = texelFetch( tex, p, 0);
	return (RGB_SOFT * c[1] - c[0] - c[2]) / (RGB_SOFT - 2);
}

#endif
#endif