prizm 0.3.1.0 → 0.3.1.1
raw patch · 13 files changed
+512/−210 lines, 13 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- CHANGES +8/−0
- README.md +0/−38
- README.org +36/−0
- prizm.cabal +70/−23
- src/Data/Prizm/Color.hs +77/−32
- src/Data/Prizm/Color/CIE.hs +13/−2
- src/Data/Prizm/Color/CIE/LAB.hs +61/−33
- src/Data/Prizm/Color/CIE/LCH.hs +51/−20
- src/Data/Prizm/Color/CIE/XYZ.hs +66/−31
- src/Data/Prizm/Color/SRGB.hs +67/−28
- src/Data/Prizm/Color/Transform.hs +4/−2
- src/Data/Prizm/Types.hs +6/−1
- tests/QC/CIE.hs +53/−0
CHANGES view
@@ -1,3 +1,11 @@+-*- mode: org -*-++0.3.1.1+=======++- Adding a little bit of documentation and better documentation in the+ Description field.+ 0.3.1.0 =======
− README.md
@@ -1,38 +0,0 @@-Welcome!-=====--Prizm is a haskell library for dealing with colors. Please contribute!--My inspiration for writing this was Sass and Bourbon, both implement interesting color handling functions for-use in stylesheets and I wanted the same thing for use in [Clay](http://fvisser.nl/clay/) (also for-[Bentonite](https://github.com/ixmatus/bentonite)).--## Colour Module--There is some overlap with the Haskell Colour Module that already exists; however, this library is filling a couple of-needs the other doesn't satisfy.--## Supported Algorithms--- sRGB <-> CIE XYZ-- CIE XYZ <-> CIE L\*ab-- CIE L\*ab <-> CIE L\*Ch--## Supported Functions--- Color interpolation-- Tinting / Darkening-- Lightness-- Hue-- Chroma/Saturation--All of these functions operate on color within the CIE L\*Ch representation. The percentage values may range between--100 and 100.--[General Color Formulas, Data, and Algorithms](http://www.brucelindbloom.com/index.html?Info.html)--[CIE Conversion Mathematics](http://rip94550.wordpress.com/2011/07/04/color-cielab-and-tristimulus-xyz/)--[Conversion Algorithm Sources](http://www.easyrgb.com/index.php?X=MATH&H=01)--[Good list of useful color manipulation](https://github.com/mikeemoo/ColorJizz-PHP/blob/master/src/MischiefCollective/ColorJizz/ColorJizz.php)
+ README.org view
@@ -0,0 +1,36 @@+* Welcome!+ `prizm` is a Haskell library for computing with colors. Specifically+ providing functions for transforming between different color spaces,+ interpolating colors and adjusting the tint, shade, hue, or+ lightness or a color.++ My inspiration for writing this was Sass and Bourbon, both implement+ interesting functions for computing with colors for use in+ stylesheets and I wanted the same thing for use in [[http://fvisser.nl/clay/][Clay]].++** Colour Module+ There is some overlap with the Haskell Colour Module that already+ exists; however, this library is filling a couple of needs the+ other doesn't satisfy. Primarily transformations and color+ mutations in the CIE L*Ch space.++** Supported Algorithms+ - sRGB <-> CIE XYZ+ - CIE XYZ <-> CIE L\*ab+ - CIE L\*ab <-> CIE L\*Ch++** Supported Functions+ - Color interpolation+ - Tinting / Darkening+ - Lightness+ - Hue+ - Chroma/Saturation++ All of these functions operate on color within the CIE L\*Ch+ representation. The percentage values may range between -100+ and 100.++[[http://www.brucelindbloom.com/index.html?Info.html][General Color Formulas, Data, and Algorithms]]+[[http://rip94550.wordpress.com/2011/07/04/color-cielab-and-tristimulus-xyz/][CIE Conversion Mathematics]]+[[http://www.easyrgb.com/index.php?X=MATH&H=01][Conversion Algorithm Sources]]+[[https://github.com/mikeemoo/ColorJizz-PHP/blob/master/src/MischiefCollective/ColorJizz/ColorJizz.php][Good list of useful color manipulation formulas]]
prizm.cabal view
@@ -1,29 +1,73 @@-Name: prizm-Version: 0.3.1.0-Synopsis: A haskell library for computing with colors+name: prizm+version: 0.3.1.1+synopsis: Compute with colors and differenct color spaces+homepage: https://github.com/ixmatus/prizm+license: BSD3+license-file: LICENSE+author: Parnell Springmeyer+maintainer: parnell@ixmat.us+copyright: (c) 2013 Parnell Springmeyer+category: Graphics+build-type: Simple+stability: stable+bug-reports: https://github.com/ixmatus/prizm/issues+package-url: http://hackage.haskell.org/package/prizm+tested-with: GHC == 7.6.3+cabal-version: >=1.14.0 -Description: Prizm is a library for converting between- different color representations and mutating- colors (interpolation, lightness, hue, chroma,- tint/shade). -Homepage: https://github.com/ixmatus/prizm-License: BSD3-License-file: LICENSE-Author: Parnell Springmeyer-Maintainer: ixmatus@gmail.com-Copyright: (c) 2013 Parnell Springmeyer-Category: Graphics-Build-type: Simple-Stability: alpha-Bug-reports: https://github.com/ixmatus/prizm/issues-Package-url: http://hackage.haskell.org/package/prizm-Tested-with: GHC == 7.4.2-Cabal-version: >=1.14.0+description:+ `prizm` is a Haskell library for computing with colors. Specifically+ providing functions for transforming between different color spaces,+ interpolating colors and adjusting the tint, shade, hue, or+ lightness or a color.+ .+ My inspiration for writing this was Sass and Bourbon, both implement+ interesting functions for computing with colors for use in+ stylesheets and I wanted the same thing for use in <http://fvisser.nl/clay/ Clay>.+ .+ /Colour Module/+ .+ There is some overlap with the Haskell Colour Module that already+ exists; however, this library is filling a couple of needs the+ other doesn't satisfy. Primarily transformations and color+ mutations in the CIE L*Ch space.+ .+ /Supported Algorithms & Functions/+ .+ All of these functions operate on color within the CIE L\*Ch+ representation. The percentage values may range between -100+ and 100.+ .+ * sRGB <-> CIE XYZ+ .+ * CIE XYZ <-> CIE L\*ab+ .+ * CIE L\*ab <-> CIE L\*Ch+ .+ * Color interpolation+ .+ * Tinting / Darkening+ .+ * Lightness+ .+ * Hue+ .+ * Chroma/Saturation+ .+ /Formulas, Resources, and Links/+ .+ * <http://www.brucelindbloom.com/index.html?Info.html General Color Formulas, Data, and Algorithms>+ .+ * <http://rip94550.wordpress.com/2011/07/04/color-cielab-and-tristimulus-xyz/ CIE Conversion Mathematics>+ .+ * <http://www.easyrgb.com/index.php?X=MATH&H=01 Conversion Algorithm Sources>+ .+ * <https://github.com/mikeemoo/ColorJizz-PHP/blob/master/src/MischiefCollective/ColorJizz/ColorJizz.php Good list of useful color manipulation formulas> extra-source-files: LICENSE- README.md+ README.org CHANGES source-repository head@@ -46,7 +90,7 @@ Data.Prizm.Color.Matrices.XYZ ghc-options: -Wall- + build-depends: base >= 4.5 && < 5, text >= 0.11.2.3 @@ -54,7 +98,10 @@ type: exitcode-stdio-1.0 hs-source-dirs: tests main-is: QC.hs- other-modules: QC.SRGB+ default-language: Haskell2010+ other-modules:+ QC.SRGB+ QC.CIE ghc-options: -Wall -threaded -rtsopts
src/Data/Prizm/Color.hs view
@@ -1,25 +1,67 @@-module Data.Prizm.Color (- (<|>)+-----------------------------------------------------------------------------+-- |+-- Module : Data.Prizm.Color+-- Copyright : (C) 2013 Parnell Springmeyer+-- License : BSD3+-- Maintainer : Parnell Springmeyer <parnell@ixmat.us>+-- Stability : stable+--+-- 'Data.Prizm.Color' provides functions for blending colors,+-- convenience blending ('tint' and 'shade'), and adjusting the 'hue',+-- 'lightness', or 'chroma' of a given color.+--+-- These functions require your color to be represented in @CIE+-- L*Ch@. The reason for this is because the @L*CH@ color space+-- represents colors closest to how the human eye sees them. Blending+-- and transforming a color in @L*Ch@ maintains the brightness of the+-- colors more accurately. Here is a link to a blog post by someone+-- detailing the differences and documenting them well:+-- <http://www.stuartdenman.com/improved-color-blending/ Improved Color Blending>.+--+-- NOTE: the transformations between color spaces are intensive, if+-- you have a HEX or RGB color it first needs to be transformed to the+-- 'CIEXYZ' color space, then the 'CIELAB' space, and finally to+-- 'CIELCH'.+--+-- I may at some point try to generalize the function's types but I+-- wanted to first support 'CIELCH' as the transformation format.+----------------------------------------------------------------------------+module Data.Prizm.Color+(+-- * Blending+ interpolate+, (<|>) , shade , tint+-- * Lightness, hue, and chroma , lightness , chroma , hue-, interpolate+-- * Types+, module Data.Prizm.Types ) where -import Control.Applicative hiding ((<|>))--import Data.Prizm.Types--pct :: Integer -> Double-pct = (/100) . fromIntegral . (max (-100)) . (min 100)+import Control.Applicative hiding ((<|>))+import Data.Prizm.Types -pctClamp :: Integer -> Integer-pctClamp i = max (min i 100) 0+------------------------------------------------------------------------------+-- Blending+------------------------------------------------------------------------------ -clamp :: Double -> Double -> Double-clamp i clmp = max (min i clmp) 0.0+-- | Interpolate two colors with a weight.+--+-- Weight is applied left to right, so if a weight of 25% is supplied,+-- then the color on the left will be multiplied by 25% and the second+-- color will be multiplied by 75%.+--+-- @CIE L*Ch@ is used because the interpolation between the colors is+-- more accurate than 'CIELAB', 'CIEXYZ', and S'RGB' color spaces.+interpolate :: Percent -> (CIELCH Double, CIELCH Double) -> CIELCH Double+interpolate w (a,b) =+ let w' = (pct (pctClamp w))+ (CIELCH l c h) = (-) <$> b <*> a+ a' = (*w') <$> (CIELCH l c (shortestPath h))+ in (+) <$> a' <*> a -- | Blend two colors using an interpolation value of 50%. (<|>) :: CIELCH Double -> CIELCH Double -> CIELCH Double@@ -33,36 +75,39 @@ tint :: CIELCH Double -> Percent -> CIELCH Double tint c w = interpolate (pctClamp w) (c, CIELCH 100.0 0.0 360.0) --- | Darken a color.+------------------------------------------------------------------------------+-- Lightness, hue, and chroma+------------------------------------------------------------------------------++-- | Adjust the lightness / darkness of a color. lightness :: CIELCH Double -> Percent -> CIELCH Double lightness (CIELCH l c h) w = (CIELCH (clamp (l + (100*(pct (pctClamp w)))) 100.0) c h) --- | Change the hue of a color.+-- | Adjust the hue of a color. hue :: CIELCH Double -> Percent -> CIELCH Double hue (CIELCH l c h) w = (CIELCH l c (clamp (h + (360*(pct (pctClamp w)))) 360.0)) --- | Change the saturation/chroma of a color. A maximum chroma value--- of 120 is assumed here, anything more is generally considered out--- of gamut.+-- | Adjust the saturation/chroma of a color.+--+-- A maximum chroma value of 120 is assumed here, anything more is+-- generally considered out of gamut. chroma :: CIELCH Double -> Percent -> CIELCH Double chroma (CIELCH l c h) w = (CIELCH l (clamp (c + (120*(pct (pctClamp w)))) 120.0) h) --- | Interpolate two colors--- --- Weight is applied left to right, so if a weight of 25% is supplied,--- then the color on the left will be multiplied by 25% and the second--- color will be multiplied by 75%.--- --- CIE L*Ch is used because the interpolation between the colors is--- more accurate than L*ab, XYZ, and sRGB color spaces.-interpolate :: Percent -> (CIELCH Double, CIELCH Double) -> CIELCH Double-interpolate w (a,b) =- let w' = (pct (pctClamp w))- (CIELCH l c h) = (-) <$> b <*> a- a' = (*w') <$> (CIELCH l c (shortestPath h))- in (+) <$> a' <*> a+------------------------------------------------------------------------------+-- Utility Functions+------------------------------------------------------------------------------ shortestPath :: Double -> Double shortestPath h | h > 180 = h - 360 | h < (-180) = h + 360 | otherwise = h++pct :: Integer -> Double+pct = (/100) . fromIntegral . (max (-100)) . (min 100)++pctClamp :: Integer -> Integer+pctClamp i = max (min i 100) 0++clamp :: Double -> Double -> Double+clamp i clmp = max (min i clmp) 0.0
src/Data/Prizm/Color/CIE.hs view
@@ -1,3 +1,13 @@+-----------------------------------------------------------------------------+-- |+-- Module : Data.Prizm.Color.CIE+-- Copyright : (C) 2013 Parnell Springmeyer+-- License : BSD3+-- Maintainer : Parnell Springmeyer <parnell@ixmat.us>+-- Stability : stable+--+-- Some basic utility functions for the CIE transformations.+---------------------------------------------------------------------------- module Data.Prizm.Color.CIE ( v1@@ -6,16 +16,17 @@ , transformXYZ ) where --- | exact rational of the "0.008856" value.+-- | Exact rational of the "0.008856" value. v1 :: Double v1 = (6/29) ** 3 --- | exact rational of the "7.787" value.+-- | Exact rational of the "7.787" value. v2 :: Double v2 = 1/3 * ((29/6) ** 2) -- 2deg observer, d65 illuminant -- [x,y,z]+-- | Reference white, 2deg observer, d65 illuminant. refWhite :: [Double] refWhite = [95.047, 100.000, 108.883]
src/Data/Prizm/Color/CIE/LAB.hs view
@@ -1,40 +1,64 @@+-----------------------------------------------------------------------------+-- |+-- Module : Data.Prizm.Color.CIE.LAB+-- Copyright : (C) 2013 Parnell Springmeyer+-- License : BSD3+-- Maintainer : Parnell Springmeyer <parnell@ixmat.us>+-- Stability : stable+--+-- Transformation functions and convenience functions, some imported+-- from the other modules to ease conversion between representations.+---------------------------------------------------------------------------- module Data.Prizm.Color.CIE.LAB (- toLCH-, toXYZ-, toRGB+-- * Transform to+-- ** RGB or Hex+ toRGB , toHex-, fromXYZ+-- ** CIE LCH or XYZ+, toLCH+, toXYZ++-- * Transform from+-- ** RGB or Hex+ , fromRGB , fromHex++-- ** CIE XYZ or LCH+, fromXYZ , fromLCH ) where -import Control.Applicative+import Control.Applicative -import Data.Prizm.Types-import Data.Prizm.Color.CIE (refWhite, transformXYZ)+import Data.Prizm.Color.CIE (refWhite, transformXYZ)+import Data.Prizm.Types -import qualified Data.Prizm.Color.SRGB as S-import qualified Data.Prizm.Color.CIE.XYZ as X-import {-# SOURCE #-} qualified Data.Prizm.Color.CIE.LCH as LC+import {-# SOURCE #-} qualified Data.Prizm.Color.CIE.LCH as LC+import qualified Data.Prizm.Color.CIE.XYZ as X+import qualified Data.Prizm.Color.SRGB as S -transformLCH :: Double -> Double-transformLCH v | v > 0 = (v / pi) * 180- | otherwise = 360 - ((abs v) / pi) * 180+------------------------------------------------------------------------------+-- Transform to+------------------------------------------------------------------------------ --- | @toLCH@ convert a LAB color to the LCH representation.+-- | Convenience function to convert LAB to RGB.+toRGB :: CIELAB Double -> RGB Integer+toRGB = X.toRGB . toXYZ++-- | Convenience function to convert LAB to HEX.+toHex :: CIELAB Double -> Hex+toHex = X.toHex . toXYZ++-- | Convert a LAB color to the LCH representation. toLCH :: CIELAB Double -> CIELCH Double toLCH (CIELAB l a b) = let h = transformLCH (atan2 b a)- c = sqrt ((a^2) + (b^2))+ c = sqrt ((a^(2 :: Int)) + (b^(2 :: Int))) in CIELCH l c h --- | @fromLCH@ convert a LCH color to the LAB representation.-fromLCH :: CIELCH Double -> CIELAB Double-fromLCH = LC.toLAB---- | @toXYZ@ convert a LAB color to the XYZ representation.+-- | Convert a LAB color to the XYZ representation. toXYZ :: CIELAB Double -> CIEXYZ Double toXYZ (CIELAB l a b) = let y = (l + 16) / 116@@ -43,22 +67,26 @@ [nx,ny,nz] = getZipList $ ((*) <$> ZipList ((transformXYZ) <$> [x,y,z])) <*> ZipList refWhite in CIEXYZ nx ny nz --- | @fromXYZ@ convert an XYZ color straight to LAB.-fromXYZ :: CIEXYZ Double -> CIELAB Double-fromXYZ = X.toLAB---- | @toRGB@ convenience function for converting straight to RGB.-toRGB :: CIELAB Double -> RGB Integer-toRGB = X.toRGB . toXYZ+------------------------------------------------------------------------------+-- Transform from+------------------------------------------------------------------------------ --- | @fromRGB@ convenience function for converting to LAB from RGB.+-- | Convenience function to convert RGB to LAB. fromRGB :: RGB Integer -> CIELAB Double fromRGB = X.toLAB . S.toXYZ --- | @toHex@ convenience function for converting straight to HEX.-toHex :: CIELAB Double -> Hex-toHex = X.toHex . toXYZ---- | @fromHex@ convenience function for converting to LAB from HEX.+-- | Convenience function to convert HEX to LAB. fromHex :: Hex -> CIELAB Double fromHex = X.toLAB . X.fromHex++-- | Convert an LCH color to the LAB representation.+fromLCH :: CIELCH Double -> CIELAB Double+fromLCH = LC.toLAB++-- | Convert an XYZ color to LAB.+fromXYZ :: CIEXYZ Double -> CIELAB Double+fromXYZ = X.toLAB++transformLCH :: Double -> Double+transformLCH v | v > 0 = (v / pi) * 180+ | otherwise = 360 - ((abs v) / pi) * 180
src/Data/Prizm/Color/CIE/LCH.hs view
@@ -1,48 +1,79 @@+-----------------------------------------------------------------------------+-- |+-- Module : Data.Prizm.Color.CIE.LCH+-- Copyright : (C) 2013 Parnell Springmeyer+-- License : BSD3+-- Maintainer : Parnell Springmeyer <parnell@ixmat.us>+-- Stability : stable+--+-- Transformation functions and convenience functions, some imported+-- from the other modules to ease conversion between representations.+---------------------------------------------------------------------------- module Data.Prizm.Color.CIE.LCH (+-- * Transform to+-- ** RGB or Hex+ toRGB+, toHex++-- ** CIE LAB or XYZ , toLAB , toXYZ-, toHex++-- * Transform from+-- ** RGB or Hex , fromRGB , fromHex++-- ** CIE LAB or XYZ , fromLAB , fromXYZ ) where -import Data.Prizm.Types+import Data.Prizm.Types -import qualified Data.Prizm.Color.CIE.LAB as LB+import {-# SOURCE #-} qualified Data.Prizm.Color.CIE.LAB as LB import qualified Data.Prizm.Color.CIE.XYZ as X --- | @toLAB@ convert an LCH color to a LAB representation.+------------------------------------------------------------------------------+-- Transform to+------------------------------------------------------------------------------++-- | Convert from LCH to RGB.+toRGB :: CIELCH Double -> RGB Integer+toRGB = X.toRGB . toXYZ++-- | Convert from LCH to Hex.+toHex :: CIELCH Double -> Hex+toHex = LB.toHex . toLAB++-- | Convert an LCH color to LAB. toLAB :: CIELCH Double -> CIELAB Double toLAB (CIELCH l c h) = let v = h * pi / 180 in CIELAB l ((cos v)*c) ((sin v)*c) --- | @fromLAB@ convenience function for converting from LAB to LCH.-fromLAB :: CIELAB Double -> CIELCH Double-fromLAB = LB.toLCH---- | @toXYZ@ convert from LCH to XYZ.+-- | Convert from LCH to XYZ. toXYZ :: CIELCH Double -> CIEXYZ Double toXYZ = LB.toXYZ . toLAB --- | @fromXYZ@ convert from XYZ to LCH.-fromXYZ :: CIEXYZ Double -> CIELCH Double-fromXYZ = X.toLCH---- | @toRGB@ convert from LCH to RGB.-toRGB :: CIELCH Double -> RGB Integer-toRGB = X.toRGB . toXYZ+------------------------------------------------------------------------------+-- Transform from+------------------------------------------------------------------------------ --- | @fromRGB@ convert from RGB to LCH.+-- | Convenience function to convert from RGB to LCH. fromRGB :: RGB Integer -> CIELCH Double fromRGB = fromLAB . LB.fromRGB -toHex :: CIELCH Double -> Hex-toHex = LB.toHex . toLAB-+-- | Convenience function to convert from Hex to LCH. fromHex :: Hex -> CIELCH Double fromHex = LB.toLCH . LB.fromHex++-- | Convenience function to convert from LAB to LCH.+fromLAB :: CIELAB Double -> CIELCH Double+fromLAB = LB.toLCH++-- | Convert from XYZ to LCH.+fromXYZ :: CIEXYZ Double -> CIELCH Double+fromXYZ = X.toLCH
src/Data/Prizm/Color/CIE/XYZ.hs view
@@ -1,67 +1,89 @@+-----------------------------------------------------------------------------+-- |+-- Module : Data.Prizm.Color.CIE.XYZ+-- Copyright : (C) 2013 Parnell Springmeyer+-- License : BSD3+-- Maintainer : Parnell Springmeyer <parnell@ixmat.us>+-- Stability : stable+--+-- Transformation functions and convenience functions, some imported+-- from the other modules to ease conversion between representations.+---------------------------------------------------------------------------- module Data.Prizm.Color.CIE.XYZ (+-- * Transform to+-- ** RGB or Hex toRGB , toRGBMatrix+, toHex++-- ** CIE LAB or LCH , toLAB , toLCH-, toHex++-- * Transform from+-- ** RGB or Hex , fromRGB , fromHex++-- ** CIE LAB or LCH , fromLAB , fromLCH ) where -import Control.Applicative+import Control.Applicative -import Data.Prizm.Types+import Data.Prizm.Types -import Data.Prizm.Color.Transform-import Data.Prizm.Color.Matrices.XYZ-import Data.Prizm.Color.CIE (v1, v2, refWhite)+import Data.Prizm.Color.CIE (refWhite, v1, v2)+import Data.Prizm.Color.Matrices.XYZ+import Data.Prizm.Color.Transform -import qualified Data.Prizm.Color.SRGB as S-import {-# SOURCE #-} qualified Data.Prizm.Color.CIE.LAB as LB-import {-# SOURCE #-} qualified Data.Prizm.Color.CIE.LCH as LC+import {-# SOURCE #-} qualified Data.Prizm.Color.CIE.LAB as LB+import {-# SOURCE #-} qualified Data.Prizm.Color.CIE.LCH as LC+import qualified Data.Prizm.Color.SRGB as S +------------------------------------------------------------------------------+-- Utilities+------------------------------------------------------------------------------ --- | @transformRGB@ transform an XYZ integer to be computed against--- the xyzToRGB matrix.+-- | Transform an XYZ integer to be computed against the xyzToRGB+-- matrix. transformRGB :: Double -> Integer transformRGB v | v > 0.0031308 = min (round ((1.055 * (v ** (1 / 2.4)) - 0.055) * 255)) 255 | otherwise = min (round ((12.92 * v) * 255)) 255 +-- | Transform an LAB integer. transformLAB :: Double -> Double transformLAB v | v > v1 = v ** (1/3) | otherwise = (v2 * v) + (16 / 116) --- | @toRGB@ convert a CIE color to an SRGB color.--- +------------------------------------------------------------------------------+-- Transform to+------------------------------------------------------------------------------++-- | Convert a CIE color to an SRGB color.+-- -- This function uses the default d65 illuminant matrix. toRGB :: CIEXYZ Double -> RGB Integer toRGB = (toRGBMatrix d65SRGB) --- | @toRGBMatrix@ convert an XYZ color to an SRGB color using a--- provided matrix.+-- | Convert an XYZ color to an SRGB color.+--+-- 'XYZtoRGB' is the pre-calculated illuminant matrix, it is+-- preferable to use 'toRG' as it uses the most "common" one. toRGBMatrix :: XYZtoRGB -> CIEXYZ Double -> RGB Integer toRGBMatrix (XYZtoRGB m) (CIEXYZ x y z) = let t = ZipList ((/100) <$> [x,y,z]) [r,g,b] = (transformRGB) <$> ((zipTransform t) <$> m) in (S.clamp) <$> RGB r g b --- | @fromRGB@ convenience function for converting to XYZ from RGB.-fromRGB :: RGB Integer -> CIEXYZ Double-fromRGB = S.toXYZ---- | @toHex@ convenience function for converting XYZ straight to HEX.+-- | Convenience function to convert XYZ to HEX. toHex :: CIEXYZ Double -> Hex toHex = S.toHex . toRGB --- | @fromHex@ convenience function for converting to XYZ from HEX.-fromHex :: Hex -> CIEXYZ Double-fromHex = S.toXYZ . S.fromHex---- | @toLAB@ convert an XYZ color to a LAB color.--- +-- | Convert an XYZ color to a LAB color.+-- -- This function uses the default reference white (2deg observer, d65 -- illuminant). toLAB :: CIEXYZ Double -> CIELAB Double@@ -73,13 +95,26 @@ b = 200 * (ty - tz) in CIELAB l a b --- | @fromLAB@ convenience function for converting to XYZ from LAB.-fromLAB :: CIELAB Double -> CIEXYZ Double-fromLAB = LB.toXYZ---- | @toLCH@ convenience function for converting XYZ straight to LAB.+-- | Convenience function to convert XYZ to LAB. toLCH :: CIEXYZ Double -> CIELCH Double toLCH = LB.toLCH . toLAB +------------------------------------------------------------------------------+-- Transform from+------------------------------------------------------------------------------++-- | Convenience function to convert RGB to XYZ.+fromRGB :: RGB Integer -> CIEXYZ Double+fromRGB = S.toXYZ++-- | Convenience function to convert HEX to XYZ.+fromHex :: Hex -> CIEXYZ Double+fromHex = S.toXYZ . S.fromHex++-- | Convenience function to convert LAB to XYZ.+fromLAB :: CIELAB Double -> CIEXYZ Double+fromLAB = LB.toXYZ++-- | Convenience function to convert LCH to XYZ. fromLCH :: CIELCH Double -> CIEXYZ Double fromLCH = LC.toXYZ
src/Data/Prizm/Color/SRGB.hs view
@@ -1,26 +1,44 @@+-----------------------------------------------------------------------------+-- |+-- Module : Data.Prizm.Color.SRGB+-- Copyright : (C) 2013 Parnell Springmeyer+-- License : BSD3+-- Maintainer : Parnell Springmeyer <parnell@ixmat.us>+-- Stability : stable+--+-- Transformation functions and convenience functions to do the base+-- conversion between S'RGB' and 'CIEXYZ'.+---------------------------------------------------------------------------- module Data.Prizm.Color.SRGB (+-- * Convert to CIEXYZ toXYZ , toXYZMatrix++-- * Convert to Hex , toHex , fromHex , clamp ) where -import Numeric (showHex)+import Numeric (showHex) -import Data.Monoid-import Data.Prizm.Types-import Data.Prizm.Color.Transform-import Data.Prizm.Color.Matrices.RGB+import Data.Monoid+import Data.Prizm.Color.Matrices.RGB+import Data.Prizm.Color.Transform+import Data.Prizm.Types -import Data.String-import qualified Data.Text as T-import Data.Text.Read as R+import Data.String+import qualified Data.Text as T+import Data.Text.Read as R -import Control.Applicative+import Control.Applicative --- | @rgbTransform@ transform an RGB integer to be computed against+------------------------------------------------------------------------------+-- Utilities+------------------------------------------------------------------------------++-- | Transform an RGB integer to be computed against -- a matrix. transform :: Integer -> Double transform v | dv > 0.04045 = (((dv + 0.055) / ap) ** 2.4) * 100@@ -28,13 +46,23 @@ where dv = fromIntegral v / 255 ap = 1.0 + 0.055 --- | @toHex@ convert an sRGB value to hexadecimal.-toHex :: RGB Integer -> Hex-toHex = showRGB+-- | Clamp an integer with an upper-bound of 255 (the maximum RGB+-- value).+clamp :: Integer -> Integer+clamp i = max (min i 255) 0 -fromHex :: Hex -> RGB Integer-fromHex = parse . fromString+-- All credit for the below three functions go to the HSColour module. +-- | Show a colour in hexadecimal form, e.g. \"#00aaff\"+showRGB :: RGB Integer -> Hex+showRGB c =+ (("#"++) . showHex2 r' . showHex2 g' . showHex2 b') ""+ where+ RGB r' g' b' = c+ showHex2 x | x <= 0xf = ("0"++) . showHex x+ | otherwise = showHex x++-- | Parse a 'Hex' into an 'RGB' type. parse :: T.Text -> RGB Integer parse t = case T.uncons t of@@ -52,27 +80,38 @@ isHex a = (a >= 'a' && a <= 'f') || (a >= 'A' && a <= 'F') || (a >= '0' && a <= '9') err = error "Invalid color string" --- | @toXYZ@ convert an sRGB value to a CIE XYZ value.+------------------------------------------------------------------------------+-- Convert to XYZ+------------------------------------------------------------------------------++-- | Convert an S'RGB' value to a 'CIEXYZ' value. toXYZ :: RGB Integer -> CIEXYZ Double toXYZ = (toXYZMatrix d65SRGB) +-- | Convert an S'RGB' value to a 'CIEXYZ' given a pre-calculated+-- illuminant matrix.+--+-- It is recommended to use 'toXYZ' as it uses the most common+-- illuminant matrix. toXYZMatrix :: RGBtoXYZ -> RGB Integer -> CIEXYZ Double toXYZMatrix (RGBtoXYZ m) (RGB r g b) = let t = ZipList (transform <$> (clamp <$> [r,g,b])) [x,y,z] = (roundN 3) <$> ((zipTransform t) <$> m) in CIEXYZ x y z -clamp :: Integer -> Integer-clamp i = max (min i 255) 0+------------------------------------------------------------------------------+-- Convert to Hex+------------------------------------------------------------------------------ -{- All credit for the below three functions go to the HSColour- module-}+-- | Convert an S'RGB' value to 'Hex'.+toHex :: RGB Integer -> Hex+toHex = showRGB --- |Show a colour in hexadecimal form, e.g. \"#00aaff\"-showRGB :: RGB Integer -> Hex-showRGB c =- (("#"++) . showHex2 r' . showHex2 g' . showHex2 b') ""- where- RGB r' g' b' = c- showHex2 x | x <= 0xf = ("0"++) . showHex x- | otherwise = showHex x+-- | Convert a 'Hex' to an S'RGB'.+fromHex :: Hex -> RGB Integer+fromHex = parse . fromString+++++
src/Data/Prizm/Color/Transform.hs view
@@ -2,13 +2,15 @@ module Data.Prizm.Color.Transform where -import Control.Applicative+import Control.Applicative +-- | Round a number to the Nth place. roundN :: Integer -> Double -> Double roundN n num = (fromInteger $ round $ num * (10^n)) / (10.0^^n) +-- | Truncate a number to the Nth place. truncateN :: Integer -> Double -> Double-truncateN n num = (fromInteger $ truncate $ num * (10^n)) / (10.0^^n)+truncateN = roundN zipTransform :: ZipList Double -> [Double] -> Double zipTransform tv m = sum $ getZipList $ (*) <$> ZipList m <*> tv
src/Data/Prizm/Types.hs view
@@ -1,10 +1,14 @@ module Data.Prizm.Types where -import Control.Applicative+import Control.Applicative +-- | Working space matrix to convert from sRGB to CIE XYZ newtype RGBtoXYZ = RGBtoXYZ [[Double]] deriving (Eq, Ord, Show)++-- | Working space matrix to convert from CIE XYZ to sRGB newtype XYZtoRGB = XYZtoRGB [[Double]] deriving (Eq, Ord, Show) +-- | Hex format color code, eg '#AB9D92' type Hex = String type Percent = Integer@@ -22,6 +26,7 @@ deriving (Eq, Ord, Show) -- | Functor instances+ instance Functor RGB where fmap f (RGB r g b) = (RGB (f r) (f g) (f b))
+ tests/QC/CIE.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE FlexibleInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module QC.CIE (tests) where++import Test.Framework (Test)+import Test.Framework.Providers.QuickCheck2 (testProperty)+import Test.QuickCheck++import Control.Applicative+import Control.Monad++import Data.Prizm.Color.CIE.LAB as LB+import Data.Prizm.Color.CIE.LCH as LC+import Data.Prizm.Color.CIE.XYZ as X+import Data.Prizm.Color.Transform+import Data.Prizm.Types++instance Arbitrary (CIEXYZ Double) where+ arbitrary = liftM3 CIEXYZ (choose (0, 95.047)) (choose (0, 100.000)) (choose (0, 108.883))++instance Arbitrary (CIELAB Double) where+ arbitrary = liftM3 CIELAB (choose (0, 100)) (choose ((-129), 129)) (choose ((-129), 129))++rN :: Double -> Double+rN = roundN 11++-- | This really needs applicatives here - I need to redefine the+-- types so that I can fmap functions over the color values.+--+-- This QuickCheck test guarantees a lossless conversion between CIE+-- XYZ and CIE L*a*b* at a precision of 11 decimal places.+xyz2LAB :: CIEXYZ Double -> Bool+xyz2LAB v =+ let nv = rN <$> v+ in (rN <$> LB.toXYZ(X.toLAB nv)) == nv++lab2XYZ :: CIELAB Double -> Bool+lab2XYZ v =+ let nv = rN <$> v+ in (rN <$> X.toLAB(LB.toXYZ nv)) == nv++lab2LCH :: CIELAB Double -> Bool+lab2LCH v =+ let nv = rN <$> v+ in (rN <$> LC.toLAB(LB.toLCH nv)) == nv++tests :: [Test]+tests = [+ testProperty "CIE XYZ <-> CIE L*a*b*" xyz2LAB,+ testProperty "CIE L*ab <-> CIE XYZ" lab2XYZ,+ testProperty "CIE L*ab <-> CIE L*Ch" lab2LCH+ ]