SAI-like Color Adjustments

I did my best to copy the color adjustments from the drawaing program that I use, Paint Tool SAI 2. I also included invert and some tint modes. 

Shader code
shader_type canvas_item;

group_uniforms Color_Adjustments;
uniform bool invert = false;

group_uniforms Color_Adjustments.Hue_Saturation;
uniform float hue : hint_range(-1.0, 1.0, 0.1) = 0.0;
uniform float saturation : hint_range(-1.0, 1.0, 0.1) = 0.0;
uniform float luminance : hint_range(-1.0, 1.0, 0.1) = 0.0;
uniform bool colorize = false;

group_uniforms Color_Adjustments.Brightness_Contrast;
uniform float brightness : hint_range(-1.0, 1.0, 0.1) = 0.0;
uniform float contrast : hint_range(-1.0, 1.0, 0.1) = 0.0;
uniform float depth : hint_range(-1.0, 1.0, 0.1) = 0.0;

group_uniforms Tint;
uniform vec3 tint : source_color;
uniform float tint_strength : hint_range(0.0, 1.0, 0.1) = 0.0;
uniform int tint_mode : hint_enum("Mix", "Multiply", "Divide", "Add", "Subtract") = 0;
group_uniforms;

// Converts an RGB color to HSV.
vec3 rgb_to_hsv(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);
}

// Converts an HSV color back to RGB.
vec3 hsv_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);
}

// Converts an HSV color to HSL.
vec3 hsv_to_hsl(vec3 c) 
{
	float l = 0.5 * c.z * (2.0 - c.y);
	float s = l > 0.0 && l < 1.0 ? c.z * c.y / (1.0 - abs(2.0 * l - 1.0)) : 0.0;
	return vec3(c.x, s, l);
}

// Converts an HSL color to HSV.
vec3 hsl_to_hsv(vec3 c) 
{
	float v = (2.0 * c.z + c.y * (1.0 - abs(2.0 * c.z - 1.0))) / 2.0;
	float s = 2.0 * (v - c.z) / v;
	return vec3(c.x, s, v);
}

// Converts an RGB color to HSL.
vec3 rgb_to_hsl(vec3 c) 
{
	float vmax = max(c.r, max(c.g, c.b));
	float vmin = min(c.r, min(c.g, c.b));
	float h, s, l = (vmax + vmin) / 2.0;
	float d = vmax - vmin;
	if (d < 0.00001) { return vec3(0.0, 0.0, l); }
	s = l > 0.5 ? d / (2.0 - vmax - vmin) : d / (vmax + vmin);
	if (vmax == c.r) { h = (c.g - c.b) / d + (c.g < c.b ? 6.0 : 0.0); }
	if (vmax == c.g) { h = (c.b - c.r) / d + 2.0; }
	if (vmax == c.b) { h = (c.r - c.g) / d + 4.0; }
	h /= 6.0;
	return vec3(h, s, l);
}

// Psudeo-private helper function for HSL to RBG function.
float _hsl_hue_to_rgb(float p, float q, float t) 
{
	if (t < 0.0) t += 1.0;
	if (t > 1.0) t -= 1.0;
	if (t < 1.0 / 6.0) { return p + (q - p) * 6.0 * t; }
	if (t < 1.0 / 2.0) { return q; }
	if (t < 2.0 / 3.0) { return p + (q - p) * (2.0 / 3.0 - t) * 6.0; }
	return p;
}

// Converts and HSL color to RGB.
vec3 hsl_to_rgb(vec3 c) 
{
	float r, g, b;
	if (c.y < 0.00001) { r = g = b = c.z; }
	else {
		float q = c.z < 0.5 ? c.z * (1.0 + c.y) : c.z + c.y - c.z * c.y;
		float p = 2.0 * c.z - q;
		r = _hsl_hue_to_rgb(p, q, c.x + 1.0 / 3.0);
		g = _hsl_hue_to_rgb(p, q, c.x);
		b = _hsl_hue_to_rgb(p, q, c.x - 1.0 / 3.0); }
	return vec3(r, g, b);
}

void fragment() 
{
	// Get the sprite's texture color.
	vec4 input_color = texture(TEXTURE, UV);
	
	// Drop alpha because we won't be using it.
	vec3 color_rgb = input_color.rgb;
	
	// Luminosity has to come before saturation (mix black or white).
	vec3 color_l = luminance < 0.0 ? vec3(0.0, 0.0, 0.0) : vec3(1.0, 1.0, 1.0);
	// If inverted, we should switch our values.
	color_l = !invert ? color_l : vec3(1.0 - color_l.r, 1.0 - color_l.g, 1.0 - color_l.b);
	color_rgb = mix(color_rgb, color_l, abs(luminance));
	
	// RGB depth control to desaturate.
	if (depth < 0.0) 
	{ 
		vec3 desaturated = vec3(dot(color_rgb, vec3(0.299, 0.587, 0.114)));
		color_rgb = mix(color_rgb, desaturated, abs(depth)); 
	}
	
	// Convert to HSL to shift the hue and saturation.
	vec3 color_hsl = rgb_to_hsl(color_rgb);
	
	// It should be safe to shift the hue first.
	color_hsl.x += mod(hue, 1.0);
	
	// HSL saturation, not HSV!! 
	if (color_hsl.y > 0.01)
	{
		float y = saturation < 0.0 ? 0.0 : 1.0;
		color_hsl.y = mix(color_hsl.y, y, abs(saturation));
	}
	
	// Colorize removes saturation and replaces with red (or whatever color we're shifted to).
	if (colorize)
	{
		color_hsl.y = min(0.5 + saturation, 1.0);
		color_hsl.x = 0.0 + hue;
	}
	
	// I almost forgot to convert to HSV.
	vec3 color_hsv = hsl_to_hsv(color_hsl);
	
	// Increase color depth; this is just HSV saturaion. 
	if (depth > 0.0) { color_hsv.y *= 1.0 + depth * 3.2; }
	
	// Convert back to RGB for the rest of the modifiers.
	color_rgb = hsv_to_rgb(color_hsv);
	
	// Brightness and contrast should be okay to go next.
	// If inverted, we should switch our values
	vec3 brightness_color = !invert ? vec3(1.0, 1.0, 1.0) : vec3(-1.0, -1.0, -1.0);
	color_rgb += brightness_color * brightness;
	
	// Only scale contrast if we are > 1.0.
	float contrast_multiplier = contrast < 0.0 ? 1.0 + contrast : 1.0 + contrast * 4.0;
	
	// Adjust color contrast around 0.5.
	color_rgb = (color_rgb - 0.5) * contrast_multiplier + 0.5; 
	
	// Try inverting the color.
	if (invert) 
	{
		// There may be some undefiend colors after all of the ulterations.
		color_rgb = clamp(color_rgb, vec3(0.0, 0.0, 0.0), vec3(1.0, 1.0, 1.0));
		
		// 1.0 - x is an easy way to flip 1s and 0s.
		color_rgb = vec3(1.0 - color_rgb.r, 1.0 - color_rgb.g, 1.0 - color_rgb.b);
	}
	
	// Then tint the color (R,G,B control is not necessary, since tint modes accomplish the same goal).
	
	// Mixes the colors together smoothly, resulting in a solid tint colored image at 1.0.
	if (tint_mode == 0) { color_rgb =  mix(color_rgb, tint, tint_strength); }
	
	// Multiplies the colors together, retaining visual contrast while darkening the base color.
	if (tint_mode == 1) { color_rgb =  color_rgb * mix(vec3(1.0, 1.0, 1.0), tint, tint_strength); }
	
	// Divides the colors, retaining visual contrast but negating the selected color and lightening.
	if (tint_mode == 2) { color_rgb =  color_rgb / max(vec3(0.00001, 0.00001, 0.00001), mix(vec3(1.0, 1.0, 1.0), tint, tint_strength)); }
	
	// Adds the colors together, creating a lightening effect (colors move toward white).
	if (tint_mode == 3) { color_rgb =  color_rgb + tint * tint_strength; }
	
	// Subtracts the colors, negating the selected color and creating a darkening effect (colors move toward black).
	if (tint_mode == 4) { color_rgb =  color_rgb - tint * tint_strength; }
	
	COLOR.rgb = color_rgb;
}
Live Preview
Tags
brightness, colorize, contrast, depth, hue, invert, luminance, sai, sai2, saturation, tint
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from pk4uf

Related shaders

guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments