{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ViewPatterns #-}
#if __GLASGOW_HASKELL__ >= 800
  {-# OPTIONS_GHC -Wno-redundant-constraints #-}
{-# LANGUAGE MonoLocalBinds #-}
#endif
-- |
-- Module      : Graphics.ColorSpace
-- Copyright   : (c) Alexey Kuleshevich 2018-2019
-- License     : BSD3
-- Maintainer  : Alexey Kuleshevich <lehins@yandex.ru>
-- Stability   : experimental
-- Portability : non-portable
--
module Graphics.ColorSpace (
  -- * Pixels
  -- ** Family of Pixels
  -- | Pixel is a type family for all available color spaces. Below is the
  -- listed of all class instances, that pixels are installed in, as well as all
  -- pixel constructors.
  --
  -- >>> :t (PixelY 0) -- Black pixel in Luma
  -- (PixelY 0) :: Num e => Pixel Y e
  -- >>> PixelRGB 255 0 0 :: Pixel RGB Word8 -- Red pixel in RGB
  -- <RGB:(255|0|0)>
  -- >>> PixelRGB 1 0 0 :: Pixel RGB Double -- Same red pixel in RGB with Double precision.
  -- <RGB:(1.0|0.0|0.0)>
  -- >>> (PixelRGB 255 0 0 :: Pixel RGB Word8) == (toWord8 <$> (PixelRGB 1 0 0 :: Pixel RGB Double))
  -- True
  --
  Pixel(..)
  , ColorSpace(..)
  , AlphaSpace(..)
  -- ** Luma (gray scale)
  -- | Conversion to Luma from other color spaces.
  , toPixelY
  , toPixelYA
  -- ** RGB
  -- | Conversion to RGB from other color spaces.
  , toPixelRGB
  , toPixelRGBA
  -- ** HSI
  -- | Conversion to HSI from other color spaces.
  , toPixelHSI
  , toPixelHSIA
  -- ** CMYK
  -- | Conversion to CMYK from other color spaces.
  , toPixelCMYK
  , toPixelCMYKA
  -- ** YCbCr
  -- | Conversion to YCbCr from other color spaces.
  , toPixelYCbCr
  , toPixelYCbCrA
  -- ** Binary
  -- | This is a Binary colorspace, pixel's of which can be created using
  -- these __/constructors/__:
  --
  --   [@'on'@] Represents value @1@ or 'True'. It's a foreground pixel and is
  --   displayed in black.
  --
  --   [@'off'@] Represents value @0@ or 'False'. It's a background pixel and is
  --   displayed in white.
  --
  -- Note, that values are inverted before writing to or reading from file, since
  -- grayscale images represent black as a @0@ value and white as @1@ on a
  -- @[0,1]@ scale.
  --
  -- Binary pixels also behave as binary numbers with a size of 1-bit, for instance:
  --
  -- >>> on + on -- equivalent to: 1 .|. 1
  -- <Binary:(1)>
  -- >>> (on + on) * off -- equivalent to: (1 .|. 1) .&. 0
  -- <Binary:(0)>
  -- >>> (on + on) - on
  -- <Binary:(0)>
  --
  , toPixelBinary
  , fromPixelBinary
  , module Graphics.ColorSpace.Binary
  -- ** Complex
  , module Graphics.ColorSpace.Complex
  -- ** X
  , toPixelsX
  , fromPixelsX
  -- * ColorSpace
  -- ** Operations on Pixels
  , eqTolPx
  -- ** Luma
  ,Y(..), YA(..),
  ToY, ToYA,
  -- ** RGB
  RGB(..), RGBA(..),
  ToRGB, ToRGBA,
  -- ** HSI
  HSI(..), HSIA(..),
  ToHSI, ToHSIA,
  -- ** CMYK
  CMYK(..), CMYKA(..),
  ToCMYK, ToCMYKA,
  -- ** YCbCr
  YCbCr(..), YCbCrA(..),
  ToYCbCr, ToYCbCrA,
  -- ** X
  X(..),
  -- * Precision
  -- ** Pixel
  Elevator(..),
  toWord8,
  toWord16,
  toWord32,
  toWord64,
  toFloat,
  toDouble,
  fromDouble,
  -- ** Componenet
  Word8, Word16, Word32, Word64
  ) where

import Data.Word
import Graphics.ColorSpace.Binary
import Graphics.ColorSpace.CMYK
import Graphics.ColorSpace.Complex
import Graphics.ColorSpace.HSI
import Graphics.ColorSpace.Internal
import Graphics.ColorSpace.RGB
import Graphics.ColorSpace.X
import Graphics.ColorSpace.Y
import Graphics.ColorSpace.YCbCr


-- -- Binary:

-- | Convert to a `Binary` pixel.
toPixelBinary :: ColorSpace cs e => Pixel cs e -> Pixel X Bit
toPixelBinary px = if px == 0 then on else off


-- | Convert a Binary pixel to Luma pixel
fromPixelBinary :: Pixel X Bit -> Pixel Y Word8
fromPixelBinary b = PixelY $ if isOn b then minBound else maxBound
{-# INLINE fromPixelBinary #-}


-- | Check weather two Pixels are equal within a tolerance. Useful for comparing
-- pixels with `Float` or `Double` precision.
eqTolPx :: (ColorSpace cs e, Ord e) =>
           e -> Pixel cs e -> Pixel cs e -> Bool
eqTolPx !tol = foldlPx2 comp True
  where comp !acc !e1 !e2 = acc && max e1 e2 - min e1 e2 <= tol
        {-# INLINE comp #-}
{-# INLINE eqTolPx #-}

-- ToY

-- | Conversion to Luma color space.
class ColorSpace cs e => ToY cs e where

  -- | Convert a pixel to Luma pixel.
  toPixelY :: Pixel cs e -> Pixel Y Double


instance Elevator e => ToY X e where
  toPixelY (PixelX y) = PixelY $ eToDouble y
  {-# INLINE toPixelY #-}

instance Elevator e => ToY Y e where
  toPixelY (PixelY y) = PixelY $ eToDouble y
  {-# INLINE toPixelY #-}

instance Elevator e => ToY YA e where
  toPixelY (PixelYA y _) = PixelY $ eToDouble y
  {-# INLINE toPixelY #-}

-- | Computes Luma: @ Y' = 0.299 * R' + 0.587 * G' + 0.114 * B' @
instance Elevator e => ToY RGB e where
  toPixelY (toDouble -> PixelRGB r g b) = PixelY (0.299*r + 0.587*g + 0.114*b)
  {-# INLINE toPixelY #-}

instance Elevator e => ToY RGBA e where
  toPixelY = toPixelY . dropAlpha
  {-# INLINE toPixelY #-}

instance Elevator e => ToY HSI e where
  toPixelY = toPixelY . toPixelRGB . toDouble
  {-# INLINE toPixelY #-}

instance Elevator e => ToY HSIA e where
  toPixelY = toPixelY . dropAlpha
  {-# INLINE toPixelY #-}

instance Elevator e => ToY CMYK e where
  toPixelY = toPixelY . toPixelRGB . toDouble
  {-# INLINE toPixelY #-}

instance Elevator e => ToY CMYKA e where
  toPixelY = toPixelY . toPixelRGB . toDouble . dropAlpha
  {-# INLINE toPixelY #-}

instance Elevator e => ToY YCbCr e where
  toPixelY (PixelYCbCr y _ _) = PixelY $ eToDouble y
  {-# INLINE toPixelY #-}

instance Elevator e => ToY YCbCrA e where
  toPixelY (PixelYCbCrA y _ _ _) = PixelY $ eToDouble y
  {-# INLINE toPixelY #-}


-- ToYA

-- | Conversion to Luma from another color space.
class ToY cs e => ToYA cs e where

  -- | Convert a pixel to Luma pixel with Alpha.
  toPixelYA :: Pixel cs e -> Pixel YA Double
  toPixelYA = addAlpha 1 . toPixelY
  {-# INLINE toPixelYA #-}


instance ToYA X Bit where
  toPixelYA (PixelX y) = PixelYA (eToDouble y) 1
  {-# INLINE toPixelYA #-}


instance ToY Y e => ToYA Y e

instance Elevator e => ToYA YA e where
  toPixelYA = toDouble
  {-# INLINE toPixelYA #-}

instance ToY RGB e => ToYA RGB e

instance Elevator e => ToYA RGBA e where
  toPixelYA !px = addAlpha (eToDouble $ getAlpha px) (toPixelY (dropAlpha px))
  {-# INLINE toPixelYA #-}

instance ToY HSI e => ToYA HSI e

instance Elevator e => ToYA HSIA e where
  toPixelYA !px = addAlpha (eToDouble $ getAlpha px) (toPixelY (dropAlpha px))
  {-# INLINE toPixelYA #-}

instance ToY CMYK e => ToYA CMYK e

instance Elevator e => ToYA CMYKA e where
  toPixelYA !px = addAlpha (eToDouble $ getAlpha px) (toPixelY (dropAlpha px))
  {-# INLINE toPixelYA #-}

instance ToY YCbCr e => ToYA YCbCr e

instance Elevator e => ToYA YCbCrA e where
  toPixelYA !px = addAlpha (eToDouble $ getAlpha px) (toPixelY (dropAlpha px))
  {-# INLINE toPixelYA #-}

-- ToRGB



-- | Conversion to `RGB` color space.
class ColorSpace cs e => ToRGB cs e where

  -- | Convert to an `RGB` pixel.
  toPixelRGB :: Pixel cs e -> Pixel RGB Double


instance ToRGB X Bit where
  toPixelRGB (PixelX b) = pure $ eToDouble b
  {-# INLINE toPixelRGB #-}

instance Elevator e => ToRGB Y e where
  toPixelRGB (PixelY g) = pure $ eToDouble g
  {-# INLINE toPixelRGB #-}

instance Elevator e => ToRGB YA e where
  toPixelRGB = toPixelRGB . dropAlpha
  {-# INLINE toPixelRGB #-}

instance Elevator e => ToRGB RGB e where
  toPixelRGB = toDouble
  {-# INLINE toPixelRGB #-}

instance Elevator e => ToRGB RGBA e where
  toPixelRGB = toDouble . dropAlpha
  {-# INLINE toPixelRGB #-}

instance Elevator e => ToRGB HSI e where
  toPixelRGB (toDouble -> PixelHSI h' s i) = getRGB (h'*2*pi) where
    !is = i*s
    !second = i - is
    errorHue = error $ "HSI pixel is not properly scaled, Hue: " ++ show h'
    getFirst !a !b = i + is*cos a/cos b
    {-# INLINE getFirst #-}
    getThird !v1 !v2 = i + 2*is + v1 - v2
    {-# INLINE getThird #-}
    getRGB h
      | h < 0      = errorHue
      | h < 2*pi/3 = let !r = getFirst h (pi/3 - h)
                         !b = second
                         !g = getThird b r
                     in PixelRGB r g b
      | h < 4*pi/3 = let !g = getFirst (h - 2*pi/3) (h + pi)
                         !r = second
                         !b = getThird r g
                     in PixelRGB r g b
      | h < 2*pi   = let !b = getFirst (h - 4*pi/3) (2*pi - pi/3 - h)
                         !g = second
                         !r = getThird g b
                     in PixelRGB r g b
      | otherwise  = errorHue
    {-# INLINE getRGB #-}
  {-# INLINE toPixelRGB #-}

instance Elevator e => ToRGB HSIA e where
  toPixelRGB = toPixelRGB . dropAlpha
  {-# INLINE toPixelRGB #-}


instance Elevator e => ToRGB YCbCr e where
  toPixelRGB (toDouble -> PixelYCbCr y cb cr) = PixelRGB r g b where
    !r = clamp01 (y                      +   1.402*(cr - 0.5))
    !g = clamp01 (y - 0.34414*(cb - 0.5) - 0.71414*(cr - 0.5))
    !b = clamp01 (y +   1.772*(cb - 0.5))
  {-# INLINE toPixelRGB #-}

instance Elevator e => ToRGB YCbCrA e where
  toPixelRGB = toPixelRGB . dropAlpha
  {-# INLINE toPixelRGB #-}

instance Elevator e => ToRGB CMYK e where
  toPixelRGB (toDouble -> PixelCMYK c m y k) = PixelRGB r g b where
    !r = (1-c)*(1-k)
    !g = (1-m)*(1-k)
    !b = (1-y)*(1-k)
  {-# INLINE toPixelRGB #-}

instance Elevator e => ToRGB CMYKA e where
  toPixelRGB = toPixelRGB . dropAlpha
  {-# INLINE toPixelRGB #-}


-- ToRGBA

-- | Conversion to `RGBA` from another color space with Alpha channel.
class ToRGB cs e => ToRGBA cs e where

  -- | Convert to an `RGBA` pixel.
  toPixelRGBA :: Pixel cs e -> Pixel RGBA Double
  toPixelRGBA = addAlpha 1 . toPixelRGB
  {-# INLINE toPixelRGBA #-}


instance ToRGBA X Bit

instance ToRGB Y e => ToRGBA Y e

instance Elevator e => ToRGBA YA e where
  toPixelRGBA !px = addAlpha (eToDouble $ getAlpha px) (toPixelRGB (dropAlpha px))
  {-# INLINE toPixelRGBA #-}

instance ToRGB RGB e => ToRGBA RGB e

instance Elevator e => ToRGBA RGBA e where
  toPixelRGBA = toDouble
  {-# INLINE toPixelRGBA #-}

instance ToRGB HSI e => ToRGBA HSI e

instance Elevator e => ToRGBA HSIA e where
  toPixelRGBA !px = addAlpha (eToDouble $ getAlpha px) (toPixelRGB (dropAlpha px))
  {-# INLINE toPixelRGBA #-}

instance ToRGB CMYK e => ToRGBA CMYK e

instance Elevator e => ToRGBA CMYKA e where
  toPixelRGBA !px = addAlpha (eToDouble $ getAlpha px) (toPixelRGB (dropAlpha px))
  {-# INLINE toPixelRGBA #-}

instance ToRGB YCbCr e => ToRGBA YCbCr e

instance Elevator e => ToRGBA YCbCrA e where
  toPixelRGBA !px = addAlpha (eToDouble $ getAlpha px) (toPixelRGB (dropAlpha px))
  {-# INLINE toPixelRGBA #-}


-- ToHSI

-- | Conversion to `HSI` color space.
class ColorSpace cs e => ToHSI cs e where

  -- | Convert to an `HSI` pixel.
  toPixelHSI :: Pixel cs e -> Pixel HSI Double


instance Elevator e => ToHSI Y e where
  toPixelHSI (PixelY y) = PixelHSI 0 0 $ eToDouble y
  {-# INLINE toPixelHSI #-}

instance Elevator e => ToHSI YA e where
  toPixelHSI = toPixelHSI . dropAlpha
  {-# INLINE toPixelHSI #-}

instance Elevator e => ToHSI RGB e where
  toPixelHSI (toDouble -> PixelRGB r g b) = PixelHSI h s i where
    !h' = atan2 y x
    !h = (if h' < 0 then h' + 2*pi else h') / (2*pi)
    !s = if i == 0 then 0 else 1 - minimum [r, g, b] / i
    !i = (r + g + b) / 3
    !x = (2*r - g - b) / 2.449489742783178
    !y = (g - b) / 1.4142135623730951
  {-# INLINE toPixelHSI #-}

instance Elevator e => ToHSI RGBA e where
  toPixelHSI = toPixelHSI . dropAlpha
  {-# INLINE toPixelHSI #-}

instance Elevator e => ToHSI HSI e where
  toPixelHSI = toDouble
  {-# INLINE toPixelHSI #-}

instance Elevator e => ToHSI HSIA e where
  toPixelHSI = toPixelHSI . dropAlpha
  {-# INLINE toPixelHSI #-}

instance Elevator e => ToHSI YCbCr e where
  toPixelHSI = toPixelHSI . toPixelRGB
  {-# INLINE toPixelHSI #-}

instance Elevator e => ToHSI YCbCrA e where
  toPixelHSI = toPixelHSI . dropAlpha
  {-# INLINE toPixelHSI #-}

instance Elevator e => ToHSI CMYK e where
  toPixelHSI = toPixelHSI . toPixelRGB
  {-# INLINE toPixelHSI #-}

instance Elevator e => ToHSI CMYKA e where
  toPixelHSI = toPixelHSI . dropAlpha
  {-# INLINE toPixelHSI #-}


-- ToHSIA

-- | Conversion to `HSIA` from another color space with Alpha channel.
class ToHSI cs e => ToHSIA cs e where

  -- | Convert to an `HSIA` pixel.
  toPixelHSIA :: Pixel cs e -> Pixel HSIA Double
  toPixelHSIA = addAlpha 1 . toPixelHSI
  {-# INLINE toPixelHSIA #-}


instance ToHSI Y e => ToHSIA Y e

instance Elevator e => ToHSIA YA e where
  toPixelHSIA !px = addAlpha (eToDouble $ getAlpha px) (toPixelHSI (dropAlpha px))
  {-# INLINE toPixelHSIA #-}

instance ToHSI RGB e => ToHSIA RGB e

instance Elevator e => ToHSIA RGBA e where
  toPixelHSIA !px = addAlpha (eToDouble $ getAlpha px) (toPixelHSI (dropAlpha px))
  {-# INLINE toPixelHSIA #-}

instance ToHSI HSI e => ToHSIA HSI e

instance Elevator e => ToHSIA HSIA e where
  toPixelHSIA = toDouble
  {-# INLINE toPixelHSIA #-}

instance ToHSI CMYK e => ToHSIA CMYK e

instance Elevator e => ToHSIA CMYKA e where
  toPixelHSIA !px = addAlpha (eToDouble $ getAlpha px) (toPixelHSI (dropAlpha px))
  {-# INLINE toPixelHSIA #-}

instance ToHSI YCbCr e => ToHSIA YCbCr e

instance Elevator e => ToHSIA YCbCrA e where
  toPixelHSIA !px = addAlpha (eToDouble $ getAlpha px) (toPixelHSI (dropAlpha px))
  {-# INLINE toPixelHSIA #-}



-- ToCMYK

-- | Conversion to `CMYK` color space.
class ColorSpace cs e => ToCMYK cs e where

  -- | Convert to a `CMYK` pixel.
  toPixelCMYK :: Pixel cs e -> Pixel CMYK Double


instance Elevator e => ToCMYK Y e where
  toPixelCMYK = toPixelCMYK . toPixelRGB
  {-# INLINE toPixelCMYK #-}

instance Elevator e => ToCMYK YA e where
  toPixelCMYK = toPixelCMYK . dropAlpha
  {-# INLINE toPixelCMYK #-}

instance Elevator e => ToCMYK RGB e where
  toPixelCMYK (toDouble -> PixelRGB r g b) = PixelCMYK c m y k where
    !c = (k' - r) / k'
    !m = (k' - g) / k'
    !y = (k' - b) / k'
    !k = 1 - k'
    !k' = max r (max g b)
  {-# INLINE toPixelCMYK #-}

instance Elevator e => ToCMYK RGBA e where
  toPixelCMYK = toPixelCMYK . dropAlpha
  {-# INLINE toPixelCMYK #-}

instance Elevator e => ToCMYK HSI e where
  toPixelCMYK = toPixelCMYK . toPixelRGB
  {-# INLINE toPixelCMYK #-}

instance Elevator e => ToCMYK HSIA e where
  toPixelCMYK = toPixelCMYK . dropAlpha
  {-# INLINE toPixelCMYK #-}

instance Elevator e => ToCMYK CMYK e where
  toPixelCMYK = toDouble
  {-# INLINE toPixelCMYK #-}

instance Elevator e => ToCMYK CMYKA e where
  toPixelCMYK = toPixelCMYK . dropAlpha
  {-# INLINE toPixelCMYK #-}

instance Elevator e => ToCMYK YCbCr e where
  toPixelCMYK = toPixelCMYK . toPixelRGB
  {-# INLINE toPixelCMYK #-}

instance Elevator e => ToCMYK YCbCrA e where
  toPixelCMYK = toPixelCMYK . dropAlpha
  {-# INLINE toPixelCMYK #-}


-- ToCMYKA

-- | Conversion to `CMYKA`.
class ToCMYK cs e => ToCMYKA cs e where

  -- | Convert to a `CMYKA` pixel.
  toPixelCMYKA :: Pixel cs e -> Pixel CMYKA Double
  toPixelCMYKA = addAlpha 1 . toPixelCMYK
  {-# INLINE toPixelCMYKA #-}


instance ToCMYK Y e => ToCMYKA Y e

instance Elevator e => ToCMYKA YA e where
  toPixelCMYKA !px = addAlpha (eToDouble $ getAlpha px) (toPixelCMYK (dropAlpha px))
  {-# INLINE toPixelCMYKA #-}

instance ToCMYK RGB e => ToCMYKA RGB e

instance Elevator e => ToCMYKA RGBA e where
  toPixelCMYKA !px = addAlpha (eToDouble $ getAlpha px) (toPixelCMYK (dropAlpha px))
  {-# INLINE toPixelCMYKA #-}

instance ToCMYK HSI e => ToCMYKA HSI e

instance Elevator e => ToCMYKA HSIA e where
  toPixelCMYKA !px = addAlpha (eToDouble $ getAlpha px) (toPixelCMYK (dropAlpha px))
  {-# INLINE toPixelCMYKA #-}

instance ToCMYK CMYK e => ToCMYKA CMYK e

instance Elevator e => ToCMYKA CMYKA e where
  toPixelCMYKA = toDouble
  {-# INLINE toPixelCMYKA #-}

instance ToCMYK YCbCr e => ToCMYKA YCbCr e

instance Elevator e => ToCMYKA YCbCrA e where
  toPixelCMYKA !px = addAlpha (eToDouble $ getAlpha px) (toPixelCMYK (dropAlpha px))
  {-# INLINE toPixelCMYKA #-}




-- ToYCbCr

-- | Conversion to `YCbCr` color space.
class ColorSpace cs e => ToYCbCr cs e where

  -- | Convert to an `YCbCr` pixel.
  toPixelYCbCr :: Pixel cs e -> Pixel YCbCr Double



instance Elevator e => ToYCbCr Y e where
  toPixelYCbCr = toPixelYCbCr . toPixelRGB
  {-# INLINE toPixelYCbCr #-}

instance Elevator e => ToYCbCr YA e where
  toPixelYCbCr = toPixelYCbCr . dropAlpha
  {-# INLINE toPixelYCbCr #-}

instance Elevator e => ToYCbCr RGB e where
  toPixelYCbCr (toDouble -> PixelRGB r g b) = PixelYCbCr y cb cr where
    !y  = clamp01 (         0.299*r +    0.587*g +    0.114*b)
    !cb = clamp01 (0.5 - 0.168736*r - 0.331264*g +      0.5*b)
    !cr = clamp01 (0.5 +      0.5*r - 0.418688*g - 0.081312*b)
  {-# INLINE toPixelYCbCr #-}

instance Elevator e => ToYCbCr RGBA e where
  toPixelYCbCr = toPixelYCbCr . dropAlpha
  {-# INLINE toPixelYCbCr #-}

instance Elevator e => ToYCbCr HSI e where
  toPixelYCbCr = toPixelYCbCr . toPixelRGB
  {-# INLINE toPixelYCbCr #-}

instance Elevator e => ToYCbCr HSIA e where
  toPixelYCbCr = toPixelYCbCr . dropAlpha
  {-# INLINE toPixelYCbCr #-}

instance Elevator e => ToYCbCr YCbCr e where
  toPixelYCbCr = toDouble
  {-# INLINE toPixelYCbCr #-}

instance Elevator e => ToYCbCr YCbCrA e where
  toPixelYCbCr = toPixelYCbCr . dropAlpha
  {-# INLINE toPixelYCbCr #-}

instance Elevator e => ToYCbCr CMYK e where
  toPixelYCbCr = toPixelYCbCr . toPixelRGB
  {-# INLINE toPixelYCbCr #-}

instance Elevator e => ToYCbCr CMYKA e where
  toPixelYCbCr = toPixelYCbCr . dropAlpha
  {-# INLINE toPixelYCbCr #-}


-- ToYCbCrA


-- | Conversion to `YCbCrA` from another color space with Alpha channel.
class ToYCbCr cs e => ToYCbCrA cs e where

  -- | Convert to an `YCbCrA` pixel.
  toPixelYCbCrA :: Pixel cs e -> Pixel YCbCrA Double
  toPixelYCbCrA = addAlpha 1 . toPixelYCbCr
  {-# INLINE toPixelYCbCrA #-}


instance ToYCbCr Y e => ToYCbCrA Y e

instance Elevator e => ToYCbCrA YA e where
  toPixelYCbCrA !px = addAlpha (eToDouble $ getAlpha px) (toPixelYCbCr (dropAlpha px))
  {-# INLINE toPixelYCbCrA #-}

instance ToYCbCr RGB e => ToYCbCrA RGB e


instance ToYCbCr HSI e => ToYCbCrA HSI e

instance Elevator e => ToYCbCrA HSIA e where
  toPixelYCbCrA !px = addAlpha (eToDouble $ getAlpha px) (toPixelYCbCr (dropAlpha px))
  {-# INLINE toPixelYCbCrA #-}

instance Elevator e => ToYCbCrA RGBA e where
  toPixelYCbCrA !px = addAlpha (eToDouble $ getAlpha px) (toPixelYCbCr (dropAlpha px))
  {-# INLINE toPixelYCbCrA #-}

instance ToYCbCr CMYK e => ToYCbCrA CMYK e

instance Elevator e => ToYCbCrA CMYKA e where
  toPixelYCbCrA !px = addAlpha (eToDouble $ getAlpha px) (toPixelYCbCr (dropAlpha px))
  {-# INLINE toPixelYCbCrA #-}

instance ToYCbCr YCbCr e => ToYCbCrA YCbCr e

instance Elevator e => ToYCbCrA YCbCrA e where
  toPixelYCbCrA = toDouble
  {-# INLINE toPixelYCbCrA #-}


-- | Change pixel precision to `Word8`.
toWord8 :: (Functor (Pixel cs), Elevator e) => Pixel cs e -> Pixel cs Word8
toWord8 = fmap eToWord8
{-# INLINABLE toWord8 #-}


-- | Change pixel precision to `Word16`.
toWord16 :: (Functor (Pixel cs), Elevator e) => Pixel cs e -> Pixel cs Word16
toWord16 = fmap eToWord16
{-# INLINABLE toWord16 #-}


-- | Change pixel precision to `Word32`.
toWord32 :: (Functor (Pixel cs), Elevator e) => Pixel cs e -> Pixel cs Word32
toWord32 = fmap eToWord32
{-# INLINABLE toWord32 #-}


-- | Change pixel precision to `Word64`.
toWord64 :: (Functor (Pixel cs), Elevator e) => Pixel cs e -> Pixel cs Word64
toWord64 = fmap eToWord64
{-# INLINABLE toWord64 #-}


-- | Change pixel precision to `Float`.
toFloat :: (Functor (Pixel cs), Elevator e) => Pixel cs e -> Pixel cs Float
toFloat = fmap eToFloat
{-# INLINABLE toFloat #-}


-- | Change pixel precision to `Double`.
toDouble :: (Functor (Pixel cs), Elevator e) => Pixel cs e -> Pixel cs Double
toDouble = fmap eToDouble
{-# INLINABLE toDouble #-}



-- | Change pixel precision from `Double`.
fromDouble :: (Functor (Pixel cs), Elevator e) => Pixel cs Double -> Pixel cs e
fromDouble = fmap eFromDouble
{-# INLINABLE fromDouble #-}