-- | Utilities for converting pixel color values.
--
-- NOTE: These functions are not polymorphic in the Float type because
--       without assisatance, GHC does a bad job of converting Word8s 
--       to and from floats. 
--
module Data.Array.Repa.Algorithms.Pixel
        ( floatRmsOfRGB8
        , doubleRmsOfRGB8
        , floatLuminanceOfRGB8
        , doubleLuminanceOfRGB8
        , rgb8OfGreyFloat
        , rgb8OfGreyDouble
        , rgb8OfFloat
        , rgb8OfDouble)
where
import Data.Word


-- | Compute the root mean square of an RGB color. Result is in the range [0..1].
floatRmsOfRGB8 :: (Word8, Word8, Word8) -> Float
{-# INLINE floatRmsOfRGB8 #-}
floatRmsOfRGB8 (r, g, b)
 = let  r'      = fromIntegral (fromIntegral r :: Int) / 255
        g'      = fromIntegral (fromIntegral g :: Int) / 255
        b'      = fromIntegral (fromIntegral b :: Int) / 255
        s       = ((r' * r') + (g' * g') + (b' * b')) / 3
   in   sqrt s


-- | Compute the root mean square of an RGB color. Result is in the range [0..1].
doubleRmsOfRGB8 :: (Word8, Word8, Word8) -> Float
{-# INLINE doubleRmsOfRGB8 #-}
doubleRmsOfRGB8 (r, g, b)
 = let  r'      = fromIntegral (fromIntegral r :: Int) / 255
        g'      = fromIntegral (fromIntegral g :: Int) / 255
        b'      = fromIntegral (fromIntegral b :: Int) / 255
        s       = ((r' * r') + (g' * g') + (b' * b')) / 3
   in   sqrt s


-- | Convert an RGB color to its luminance value. Result in the range [0..1].
floatLuminanceOfRGB8 :: (Word8, Word8, Word8) -> Float
{-# INLINE floatLuminanceOfRGB8 #-}
floatLuminanceOfRGB8 (r, g, b)
 = let  r'      = fromIntegral (fromIntegral r :: Int) / 255
        g'      = fromIntegral (fromIntegral g :: Int) / 255
        b'      = fromIntegral (fromIntegral b :: Int) / 255
   in   r' * 0.3 + g' * 0.59 + b' * 0.11


-- | Convert an RGB color to its luminance value. Result in the range [0..1].
doubleLuminanceOfRGB8 :: (Word8, Word8, Word8) -> Double
{-# INLINE doubleLuminanceOfRGB8 #-}
doubleLuminanceOfRGB8 (r, g, b)
 = let  r'      = fromIntegral (fromIntegral r :: Int) / 255
        g'      = fromIntegral (fromIntegral g :: Int) / 255
        b'      = fromIntegral (fromIntegral b :: Int) / 255
   in   r' * 0.3 + g' * 0.59 + b' * 0.11


-- | Promote a value in the range [0..1] to a grey RGB8 color.
rgb8OfGreyFloat :: Float -> (Word8, Word8, Word8)
{-# INLINE rgb8OfGreyFloat #-}
rgb8OfGreyFloat x
 = let  v        = fromIntegral (truncate (x * 255) :: Int)
   in   (v, v, v)


-- | Promote a value in the range [0..1] to a grey RGB8 color.
rgb8OfGreyDouble :: Double -> (Word8, Word8, Word8)
{-# INLINE rgb8OfGreyDouble #-}
rgb8OfGreyDouble x
 = let  v        = fromIntegral (truncate (x * 255) :: Int)
   in   (v, v, v)


-- | Promote a tuple of color components to a RGB8 color. 
--   Each of the source components should be in the range [0..1].
rgb8OfFloat :: (Float, Float, Float) -> (Word8, Word8, Word8)
{-# INLINE rgb8OfFloat #-}
rgb8OfFloat (r, g, b)
 = let  r8      = fromIntegral (truncate (r * 255) :: Int)
        g8      = fromIntegral (truncate (g * 255) :: Int)
        b8      = fromIntegral (truncate (b * 255) :: Int)
   in   (r8, g8, b8)


-- | Promote a tuple of color components to a RGB8 color. 
--   Each of the source components should be in the range [0..1].
rgb8OfDouble :: (Double, Double, Double) -> (Word8, Word8, Word8)
{-# INLINE rgb8OfDouble #-}
rgb8OfDouble (r, g, b)
 = let  r8      = fromIntegral (truncate (r * 255) :: Int)
        g8      = fromIntegral (truncate (g * 255) :: Int)
        b8      = fromIntegral (truncate (b * 255) :: Int)
   in   (r8, g8, b8)