Fusion icon indicating copy to clipboard operation
Fusion copied to clipboard

sRGB Transformations

Open jalsop24 opened this issue 2 years ago • 3 comments

PR resolves #257. My goal is to start off by implementing the basic sRGB transfers. I think more discussion is needed to determine how to allow users to choose their own gamut.

This should be accounted for, at minimum the sRGB transfer functions could be added

My understanding of this is practically wrapping the Oklab to/from methods with sRGB conversions:

-- Converts a Color3 in RGB space to a Vector3 in Oklab space.
function Oklab.to(srgb: Color3): Vector3
	-- Now assume that the input Color3 is part of sRGB, so convert it to 
	-- linear before operating on it
	local rgb = sRGB.toLinear(srgb)

	local l = rgb.R * 0.4122214708 + rgb.G * 0.5363325363 + rgb.B * 0.0514459929
	local m = rgb.R * 0.2119034982 + rgb.G * 0.6806995451 + rgb.B * 0.1073969566
	local s = rgb.R * 0.0883024619 + rgb.G * 0.2817188376 + rgb.B * 0.6299787005

	local lRoot = l ^ (1/3)
	local mRoot = m ^ (1/3)
	local sRoot = s ^ (1/3)

	return Vector3.new(
		lRoot * 0.2104542553 + mRoot * 0.7936177850 - sRoot * 0.0040720468,
		lRoot * 1.9779984951 - mRoot * 2.4285922050 + sRoot * 0.4505937099,
		lRoot * 0.0259040371 + mRoot * 0.7827717662 - sRoot * 0.8086757660
	)
end

-- Converts a Vector3 in CIELAB space to a Color3 in RGB space.
-- The Color3 will be clamped by default unless specified otherwise.
function Oklab.from(lab: Vector3, unclamped: boolean?): Color3
	local lRoot = lab.X + lab.Y * 0.3963377774 + lab.Z * 0.2158037573
	local mRoot = lab.X - lab.Y * 0.1055613458 - lab.Z * 0.0638541728
	local sRoot = lab.X - lab.Y * 0.0894841775 - lab.Z * 1.2914855480

	local l = lRoot ^ 3
	local m = mRoot ^ 3
	local s = sRoot ^ 3

	local red = l * 4.0767416621 - m * 3.3077115913 + s * 0.2309699292
	local green = l * -1.2684380046 + m * 2.6097574011 - s * 0.3413193965
	local blue = l * -0.0041960863 - m * 0.7034186147 + s * 1.7076147010

	if not unclamped then
		red = math.clamp(red, 0, 1)
		green = math.clamp(green, 0, 1)
		blue = math.clamp(blue, 0, 1)
	end
	
	-- Perform the conversion back into sRGB before returning
	return sRGB.fromLinear(Color3.new(red, green, blue))
end

jalsop24 avatar Oct 01 '23 22:10 jalsop24

Currently I'm aiming for this sort of syntax:


local Oklab = Fusion.Oklab

local bgColour = Spring(Computed(function(use)
    return if use(isRed) then Oklab.new(0.600, 0.483, 0.128) else Oklab.new(0.600, −0.100, −0.490)
end), 20, 0.5)

local frame = New "Frame" {
    BackgroundColor3 = Computed(function(use) return use(bgColour):toSRGB() end)
}

jalsop24 avatar Oct 04 '23 14:10 jalsop24

New syntax for colour manipulation is outside the scope of the linked issue and should really be discussed elsewhere, please stick to the existing API surface for now. This is an internal bug where our existing contract with the user isn't being upheld - fixing this does not require changing the contract, it only requires changing our internal code to conform to it as we originally promised to users.

Other than that, I'd be happy to accept a simpler version that implements the appropriate conversions to ensure Color3 is interpreted as sRGB rather than linear RGB.

dphfox avatar Oct 15 '23 17:10 dphfox

Okay I've reverted back to a simpler implementation that focuses on the srgb <-> oklab conversion.

jalsop24 avatar Oct 15 '23 20:10 jalsop24