colour 2.3.6 → 2.3.7
raw patch · 6 files changed
+180/−12 lines, 6 filesdep +HUnitdep +test-framework-hunitdep ~QuickCheckdep ~randomPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: HUnit, test-framework-hunit
Dependency ranges changed: QuickCheck, random
API changes (from Hackage documentation)
+ Data.Colour.Ok: okLCh :: Floating a => a -> a -> a -> Colour a
+ Data.Colour.Ok: okLChView :: RealFloat a => Colour a -> (a, a, a)
+ Data.Colour.Ok: okLab :: Floating a => a -> a -> a -> Colour a
+ Data.Colour.Ok: okLabView :: Floating a => Colour a -> (a, a, a)
- Data.Colour: class AffineSpace f
+ Data.Colour: class AffineSpace (f :: Type -> Type)
- Data.Colour: class ColourOps f
+ Data.Colour: class ColourOps (f :: Type -> Type)
Files
- CHANGELOG +5/−0
- Data/Colour.hs +2/−0
- Data/Colour/Ok.hs +125/−0
- Data/Colour/SRGB.hs +5/−7
- Tests.hs +34/−0
- colour.cabal +9/−5
CHANGELOG view
@@ -1,3 +1,8 @@+New in version 2.3.7:+- Removed uses of head and tail.+- Updated dependencies (for testing).+- Added support for Oklab and Oklch colour spaces from kqr.+ New in version 2.3.6: - Minimum base of 4.13. - Locked down non-colour imports.
Data/Colour.hs view
@@ -34,6 +34,8 @@ -- -- - "Data.Colour.CIE" --+-- - "Data.Colour.Ok"+-- -- Colours can be specified in a generic 'Data.Colour.RGBSpace.RGBSpace' -- by using --
+ Data/Colour/Ok.hs view
@@ -0,0 +1,125 @@+{-+Copyright (c) 2008, 2009, 2026+Russell O'Connor, Christoffer Stjernlöf++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in+all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+THE SOFTWARE.+-}+-- | Functions for converting 'Colour' values to and from the Oklab space, and+-- convenience functions for the derived Oklch space.+-- See <https://bottosson.github.io/posts/oklab/>.+module Data.Colour.Ok+ (okLab, okLabView+ ,okLCh, okLChView+ )+where++import Data.Complex (Complex((:+)), magnitude, mkPolar, phase)+import Data.Fixed (mod')++import Data.Colour.CIE+import Data.Colour.CIE.Illuminant+import Data.Colour.Matrix++-- |Returns the Oklab coordinates of a colour, which is a+-- perceptually uniform colour space inspired by CIELAB+-- but which handles blue hues more accurately.+--+-- A white point is not specified because Oklab assumes+-- a standard 'd65' daylight illuminant.+okLabView :: Floating a => Colour a -> (a,a,a)+okLabView colour = (l,a,b)+ where+ (x,y,z) = cieXYZView colour+ lms = mult okM1 [x,y,z]+ cbrt v = signum v * (abs v)**(1/3)+ lms' = map cbrt lms+ [l,a,b] = mult (inverse okM2inv) lms'++-- |Returns the colour for given Oklab coordinates, which+-- is a perceptually uniform colour space inspired by+-- CIELAB but which handles blue hues more accurately.+--+-- A white point is not specified because Oklab assumes+-- a standard 'd65' daylight illuminant.+okLab :: Floating a => a -- ^L* coordinate (lightness)+ -> a -- ^a* coordinate+ -> a -- ^b* coordinate+ -> Colour a+okLab l a b = cieXYZ x y z+ where+ lms' = mult okM2inv [l, a, b]+ lms = map (**3) lms'+ [x,y,z] = mult (inverse okM1) lms++-- |Returns the Oklab LCh coordinates of a colour. The+-- lightness coordinate is the same as in Oklab, while+-- the chroma C and hue h are the (a,b)-coordinates+-- expressed in polar form.+okLChView :: RealFloat a => Colour a -> (a,a,a)+okLChView colour = (l,c, h `mod'` 360)+ where+ (l,a,b) = okLabView colour+ z = a :+ b+ c = magnitude z+ h = phase z * 180 / pi++-- |Constructs a colour from a lightness, chroma, and+-- hue given in LCh polar coordinates correspondong to+-- an Oklab coordinate.+okLCh :: Floating a => a -- ^L* coordinate (lightness)+ -> a -- ^C* coordinate (chroma)+ -> a -- ^h* coordinate (hue)+ -> Colour a+okLCh l c h = okLab l a b+ where+ (a :+ b) = mkPolar c (h * pi / 180)++--------------------------------------------------------------------------+{- not for export -}++-- |Converts XYZ coordinates to approximate human cone responses.+-- See <https://github.com/color-js/color.js/blob/6b487db289f7c41b78d2933d9cb83bf8c06f3c5e/scripts/oklab_matrix_maker.py>+oklms :: Fractional a => Chromaticity a -- ^White point+ -> [[a]]+oklms white_ch = zipWith f m0 lmsWhite+ where+ white = chromaColour white_ch 1.0+ (xw,yw,zw) = cieXYZView white+ lmsWhite = mult m0 [xw, yw, zw]+ f v w = (/w) <$> v+ m0 = [[0.77849780, 0.34399940, -0.12249720]+ ,[0.03303601, 0.93076195, 0.03620204]+ ,[0.05092917, 0.27933344, 0.66973739]+ ]++-- |The M1 matrix from the Oklab specification. This converts XYZ coordinates+-- to approximate human cone responses.+okM1 :: Fractional a => [[a]]+okM1 = oklms d65++-- |The inverse M2 matrix from the Oklab specification. This converts+-- perceptually uniform LAB coordinates into+-- non-linearly-transformed (l', m', s') coordinates.+-- See <https://github.com/color-js/color.js/blob/6b487db289f7c41b78d2933d9cb83bf8c06f3c5e/scripts/oklab_matrix_maker.py>+okM2inv :: Fractional a => [[a]]+okM2inv =+ [[1.0, 0.3963377774, 0.2158037573]+ ,[1.0, -0.1055613458, -0.0638541728]+ ,[1.0, -0.0894841775, -1.2914855480]+ ]
Data/Colour/SRGB.hs view
@@ -110,11 +110,11 @@ -- |Read a colour in hexadecimal form, e.g. \"#00aaff\" or \"00aaff\" sRGB24reads :: (Ord b, Floating b) => ReadS (Colour b) sRGB24reads "" = []-sRGB24reads x =+sRGB24reads x@(hdx : tlx) = [(sRGB24 a b c, c0) |(a,a0) <- readPair x', (b,b0) <- readPair a0, (c,c0) <- readPair b0] where- x' | head x == '#' = tail x+ x' | hdx == '#' = tlx | otherwise = x readPair [] = [] readPair [_] = []@@ -124,11 +124,9 @@ -- |Read a colour in hexadecimal form, e.g. \"#00aaff\" or \"00aaff\" sRGB24read :: (Ord b, Floating b) => String -> (Colour b)-sRGB24read x | length rx /= 1 || not (null (snd (head rx))) =- error "Data.Colour.SRGB.sRGB24read: no parse"- | otherwise = fst (head rx)- where- rx = sRGB24reads x+sRGB24read x = case sRGB24reads x of+ [(x,"")] -> x+ _ -> error "Data.Colour.SRGB.sRGB24read: no parse" -- |The sRGB colour space sRGBSpace :: (Ord a, Floating a) => RGBSpace a
Tests.hs view
@@ -23,13 +23,16 @@ -} module Main where +import Data.Fixed (Milli, resolution) import Data.Word (Word8) import Control.Monad (liftM, liftM2, liftM3) import Test.QuickCheck ( Arbitrary, CoArbitrary, Gen, Property , (==>), arbitrary, choose, coarbitrary, forAll )+import Test.HUnit (Assertion, (@?=)) import Test.Framework (defaultMain, defaultMainWithOpts, testGroup) import Test.Framework.Providers.QuickCheck2 (testProperty)+import Test.Framework.Providers.HUnit (testCase) import Data.Colour.Matrix import Data.Colour@@ -37,6 +40,7 @@ import Data.Colour.SRGB.Linear import Data.Colour.CIE import Data.Colour.CIE.Illuminant (d65)+import Data.Colour.Ok import Data.Colour.Names --import Data.Colour.HDTV as HDTV --import qualified Data.Colour.SDTV as SDTV@@ -161,6 +165,18 @@ (l,a,b) = cieLABView wp c cc = toSRGB24 +prop_toFromOklab :: DColour -> Bool+prop_toFromOklab c = cc (okLab l a b) == cc c+ where+ (l,a,b) = okLabView c+ cc = toSRGB24++prop_toFromOklch :: DColour -> Bool+prop_toFromOklch c = cc (okLCh l a b) == cc c+ where+ (l,a,b) = okLChView c+ cc = toSRGB24+ {- prop_fromToY'CbCr709 :: Word8 -> Word8 -> Word8 -> Bool prop_fromToY'CbCr709 y' cb cr =@@ -286,6 +302,21 @@ snd (properFraction ((h0-h1)/360)::(Integer,Rational)) == 0 && s0 == s1 && v0 == v1 +-- |Pairs of XYZ and LAB colours provided for verification in the Oklab+-- specification.+case_xyzToOklab :: Assertion+case_xyzToOklab = do+ xyzToOklab 0.950 1.000 1.089 @?= [ 1.000, 0.000, 0.000]+ xyzToOklab 1.000 0.000 0.000 @?= [ 0.450, 1.236,-0.019]+ xyzToOklab 0.000 1.000 0.000 @?= [ 0.922,-0.671, 0.263]+ xyzToOklab 0.000 0.000 1.000 @?= [ 0.153,-1.415,-0.449]+ where+ xyzToOklab x y z = rounded (okLabView (cieXYZ x y z))+ rounded (l,a,b) = map roundFixed [l,a,b] :: [Milli]+ roundFixed x = result+ where+ result = fromRational $ toRational x + (0.5/fromInteger (resolution result))+ tests = [ testProperty "matrix-mult" prop_matrixMult , testProperty "RGB-to-from" prop_toFromRGB , testProperty "RGB-from-to" prop_fromToRGB@@ -294,6 +325,8 @@ , testProperty "sRGB-to-from" prop_toFromSRGB , testProperty "sRGB-from-to" prop_fromToSRGB , testProperty "cieLAB-to-from" (prop_toFromLAB d65)+ , testProperty "Oklab-to-from" prop_toFromOklab+ , testProperty "Oklch-to-from" prop_toFromOklch -- , testProperty "Y'CbCr-709-from-to" prop_fromToY'CbCr709 -- , testProperty "Y'CbCr-601-from-to" prop_fromToY'CbCr601 , testProperty "dissolve-id" prop_disolveId@@ -326,6 +359,7 @@ , testProperty "fromToHSL" prop_fromToHSL , testProperty "toFromHSV" prop_toFromHSV , testProperty "fromToHSV" prop_fromToHSV+ , testCase "example-xyz-oklab-pairs" case_xyzToOklab ] main = defaultMain tests
colour.cabal view
@@ -1,5 +1,5 @@ Name: colour-Version: 2.3.6+Version: 2.3.7 Cabal-Version: >= 1.10 License: MIT License-file: LICENSE@@ -13,7 +13,7 @@ Colours can be blended and composed. Various colour spaces are supported. A module of colour names ("Data.Colour.Names") is provided.-Tested-with: GHC == 8.8.4+Tested-with: GHC == 9.10.3 data-files: README CHANGELOG Library@@ -28,6 +28,7 @@ Data.Colour.RGBSpace.HSL Data.Colour.RGBSpace.HSV Data.Colour.Names+ Data.Colour.Ok Other-Modules: Data.Colour.Internal Data.Colour.Chan Data.Colour.RGB@@ -39,15 +40,18 @@ main-is: Tests.hs build-depends: base >= 4.13 && < 5, colour,- QuickCheck >= 2.5 && < 2.15,- random >= 1.0 && < 1.2,+ QuickCheck >= 2.5 && < 2.16,+ random >= 1.0 && < 1.3, test-framework >= 0.8 && < 0.9,- test-framework-quickcheck2 >= 0.3 && < 0.4+ test-framework-quickcheck2 >= 0.3 && < 0.4,+ test-framework-hunit >= 0.3 && < 0.4,+ HUnit >= 1.4 && <1.7 Other-Modules: Data.Colour Data.Colour.SRGB Data.Colour.SRGB.Linear Data.Colour.CIE Data.Colour.CIE.Illuminant+ Data.Colour.Ok Data.Colour.RGBSpace Data.Colour.RGBSpace.HSL Data.Colour.RGBSpace.HSV