Skip to content

Commit

Permalink
sRGB Transformations (#283)
Browse files Browse the repository at this point in the history
Co-authored-by: Daniel P H Fox <[email protected]>
  • Loading branch information
jalsop24 and dphfox authored Apr 15, 2024
1 parent 367dab4 commit 2e19c7f
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 16 deletions.
12 changes: 6 additions & 6 deletions src/Animation/lerpType.lua
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,20 @@ local function lerpType(

elseif typeString == "Color3" then
local to, from = to :: Color3, from :: Color3
local fromLab = Oklab.to(from)
local toLab = Oklab.to(to)
return Oklab.from(
local fromLab = Oklab.fromSRGB(from)
local toLab = Oklab.fromSRGB(to)
return Oklab.toSRGB(
fromLab:Lerp(toLab, ratio),
false
)

elseif typeString == "ColorSequenceKeypoint" then
local to, from = to :: ColorSequenceKeypoint, from :: ColorSequenceKeypoint
local fromLab = Oklab.to(from.Value)
local toLab = Oklab.to(to.Value)
local fromLab = Oklab.fromSRGB(from.Value)
local toLab = Oklab.fromSRGB(to.Value)
return ColorSequenceKeypoint.new(
(to.Time - from.Time) * ratio + from.Time,
Oklab.from(
Oklab.toSRGB(
fromLab:Lerp(toLab, ratio),
false
)
Expand Down
4 changes: 2 additions & 2 deletions src/Animation/packType.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ local function packType(
)

elseif typeString == "Color3" then
return Oklab.from(
return Oklab.toSRGB(
Vector3.new(numbers[1], numbers[2], numbers[3]),
false
)

elseif typeString == "ColorSequenceKeypoint" then
return ColorSequenceKeypoint.new(
numbers[4],
Oklab.from(
Oklab.toSRGB(
Vector3.new(numbers[1], numbers[2], numbers[3]),
false
)
Expand Down
6 changes: 2 additions & 4 deletions src/Animation/unpackType.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,11 @@ local function unpackType(
return {value.X, value.Y, value.Z, axis.X, axis.Y, axis.Z, angle}

elseif typeString == "Color3" then
local value = value :: Color3
local lab = Oklab.to(value)
local lab = Oklab.fromSRGB(value)
return {lab.X, lab.Y, lab.Z}

elseif typeString == "ColorSequenceKeypoint" then
local value = value :: ColorSequenceKeypoint
local lab = Oklab.to(value.Value)
local lab = Oklab.fromSRGB(value.Value)
return {lab.X, lab.Y, lab.Z, value.Time}

elseif typeString == "DateTime" then
Expand Down
22 changes: 18 additions & 4 deletions src/Colour/Oklab.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@
See: https://bottosson.github.io/posts/oklab/
]]

local sRGB = require(script.Parent.sRGB)

local Oklab = {}

-- Converts a Color3 in RGB space to a Vector3 in Oklab space.
function Oklab.to(rgb: Color3): Vector3
-- Converts a Color3 in linear RGB space to a Vector3 in Oklab space.
function Oklab.fromLinear(rgb: Color3): Vector3

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
Expand All @@ -27,9 +30,14 @@ function Oklab.to(rgb: Color3): Vector3
)
end

-- Converts a Vector3 in CIELAB space to a Color3 in RGB space.
-- Converts a Color3 in sRGB space to a Vector3 in Oklab space.
function Oklab.fromSRGB(srgb: Color3): Vector3
return Oklab.fromLinear(sRGB.toLinear(srgb))
end

-- Converts a Vector3 in Oklab space to a Color3 in linear RGB space.
-- The Color3 will be clamped by default unless specified otherwise.
function Oklab.from(lab: Vector3, unclamped: boolean?): Color3
function Oklab.toLinear(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
Expand All @@ -51,4 +59,10 @@ function Oklab.from(lab: Vector3, unclamped: boolean?): Color3
return Color3.new(red, green, blue)
end

-- Converts a Vector3 in Oklab space to a Color3 in sRGB space.
-- The Color3 will be clamped by default unless specified otherwise.
function Oklab.toSRGB(lab: Vector3, unclamped: boolean?): Color3
return sRGB.fromLinear(Oklab.toLinear(lab, unclamped))
end

return Oklab
52 changes: 52 additions & 0 deletions src/Colour/sRGB.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
--!strict

--[[
Provides transformation functions for converting linear RGB values
into sRGB values.
RGB color channel transformations are outlined here:
https://bottosson.github.io/posts/colorwrong/#what-can-we-do%3F
]]

local sRGB = {}

-- Equivalent to f_inv. Takes a linear sRGB channel and returns
-- the sRGB channel
local function transform(channel: number): number
if channel >= 0.04045 then
return ((channel + 0.055)/(1 + 0.055))^2.4
else
return channel / 12.92
end
end

-- Equivalent to f. Takes an sRGB channel and returns
-- the linear sRGB channel
local function inverse(channel: number): number
if channel >= 0.0031308 then
return (1.055) * channel^(1.0/2.4) - 0.055
else
return 12.92 * channel
end
end

-- Uses a tranformation to convert linear RGB into sRGB.
function sRGB.fromLinear(rgb: Color3): Color3
return Color3.new(
transform(rgb.R),
transform(rgb.G),
transform(rgb.B)
)
end

-- Converts an sRGB into linear RGB using a
-- (The inverse of sRGB.fromLinear).
function sRGB.toLinear(srgb: Color3): Color3
return Color3.new(
inverse(srgb.R),
inverse(srgb.G),
inverse(srgb.B)
)
end

return sRGB

0 comments on commit 2e19c7f

Please sign in to comment.