stylix/palette-generator/Data/Colour.hs
Daniel Thwaites 671068f7f6
Add internal docs for palette generator 💡
So that I don't forget how it works :)
2022-05-02 01:12:27 +01:00

72 lines
2.7 KiB
Haskell

module Data.Colour ( LAB(..), RGB(..), deltaE, lab2rgb, rgb2lab ) where
-- | Lightness A-B
data LAB a = LAB { lightness :: a
, channelA :: a
, channelB :: a
}
-- | Red, Green, Blue
data RGB a = RGB { red :: a
, green :: a
, blue :: a
}
-- Based on https://github.com/antimatter15/rgb-lab/blob/master/color.js
deltaE :: (Floating a, Ord a) => LAB a -> LAB a -> a
deltaE (LAB l1 a1 b1) (LAB l2 a2 b2) =
let deltaL = l1 - l2
deltaA = a1 - a2
deltaB = b1 - b2
c1 = sqrt $ a1^2 + b1^2
c2 = sqrt $ a2^2 + b2^2
deltaC = c1 - c2
deltaH = deltaA^2 + deltaB^2 - deltaC^2
deltaH' = if deltaH < 0 then 0 else sqrt deltaH
sc = 1 + 0.045 * c1
sh = 1 + 0.015 * c1
deltaCkcsc = deltaC / sc
deltaHkhsh = deltaH' / sh
i = deltaL^2 + deltaCkcsc^2 + deltaHkhsh^2
in if i < 0 then 0 else sqrt i
-- | Convert a 'LAB' colour to a 'RGB' colour
lab2rgb :: (Floating a, Ord a) => LAB a -> RGB a
lab2rgb (LAB l a bx) =
let y = (l + 16) / 116
x = a / 500 + y
z = y - bx / 200
x' = 0.95047 * (if x^3 > 0.008856 then x^3 else (x - 16/116) / 7.787)
y' = if y^3 > 0.008856 then y^3 else (y - 16/116) / 7.787
z' = 1.08883 * (if z^3 > 0.008856 then z^3 else (z - 16/116) / 7.787)
r = x' * 3.2406 + y' * (-1.5372) + z' * (-0.4986)
g = x' * (-0.9689) + y' * 1.8758 + z' * 0.0415
b = x' * 0.0557 + y' * (-0.204) + z' * 1.0570
r' = if r > 0.0031308 then 1.055 * r**(1/2.4) - 0.055 else 12.92 * r
g' = if g > 0.0031308 then 1.055 * g**(1/2.4) - 0.055 else 12.92 * g
b' = if b > 0.0031308 then 1.055 * b**(1/2.4) - 0.055 else 12.92 * b
in RGB { red = max 0 (min 1 r') * 255
, green = max 0 (min 1 g') * 255
, blue = max 0 (min 1 b') * 255
}
-- | Convert a 'RGB' colour to a 'LAB' colour
rgb2lab :: (Floating a, Ord a) => RGB a -> LAB a
rgb2lab (RGB r g b) =
let r' = r / 255
g' = g / 255
b' = b / 255
r'' = if r' > 0.04045 then ((r' + 0.055) / 1.055)**2.4 else r' / 12.92
g'' = if g' > 0.04045 then ((g' + 0.055) / 1.055)**2.4 else g' / 12.92
b'' = if b' > 0.04045 then ((b' + 0.055) / 1.055)**2.4 else b' / 12.92
x = (r'' * 0.4124 + g'' * 0.3576 + b'' * 0.1805) / 0.95047
y = r'' * 0.2126 + g'' * 0.7152 + b'' * 0.0722
z = (r'' * 0.0193 + g'' * 0.1192 + b'' * 0.9505) / 1.08883
x' = if x > 0.008856 then x**(1/3) else (7.787 * x) + 16/116
y' = if y > 0.008856 then y**(1/3) else (7.787 * y) + 16/116
z' = if z > 0.008856 then z**(1/3) else (7.787 * z) + 16/116
in LAB { lightness = (116 * y') - 16
, channelA = 500 * (x' - y')
, channelB = 200 * (y' - z')
}