module Codec.Picture.Types( 
                            
                            Image( .. )
                          , MutableImage( .. )
                          , DynamicImage( .. )
                            
                          , freezeImage
                          , unsafeFreezeImage
                            
                          , Pixel8
                          , Pixel16
                          , PixelF
                          , PixelYA8( .. )
                          , PixelYA16( .. )
                          , PixelRGB8( .. )
                          , PixelRGB16( .. )
                          , PixelRGBF( .. )
                          , PixelRGBA8( .. )
                          , PixelRGBA16( .. )
                          , PixelCMYK8( .. )
                          , PixelCMYK16( .. )
                          , PixelYCbCr8( .. )
                          
                          , ColorConvertible( .. )
                          , Pixel(..)
                          
                          , ColorSpaceConvertible( .. )
                          , LumaPlaneExtractable( .. )
                          , TransparentPixel( .. )
                            
                          , pixelMap
                          , pixelFold
                          , dynamicMap
                          , dynamicPixelMap
                          , dropAlphaLayer
                          , withImage
                          , generateImage
                          , generateFoldImage
                          , gammaCorrection
                          , toneMapping
                            
                          , ColorPlane ( )
                          , PlaneRed( .. )
                          , PlaneGreen( .. )
                          , PlaneBlue( .. )
                          , PlaneAlpha( .. )
                          , PlaneLuma( .. )
                          , PlaneCr( .. )
                          , PlaneCb( .. )
                          , PlaneCyan( .. )
                          , PlaneMagenta( .. )
                          , PlaneYellow( .. )
                          , PlaneBlack( .. )
                          , extractComponent
                          , unsafeExtractComponent
                          ) where
import Control.Monad( forM_, foldM, liftM, ap )
import Control.DeepSeq( NFData( .. ) )
import Control.Monad.ST( runST )
import Control.Monad.Primitive ( PrimMonad, PrimState )
import Foreign.Storable ( Storable )
import Data.Bits( unsafeShiftL, unsafeShiftR )
import Data.Word( Word8, Word16 )
import Data.List( foldl' )
import Data.Vector.Storable ( (!) )
import qualified Data.Vector.Storable as V
import qualified Data.Vector.Storable.Mutable as M
#include "ConvGraph.hs"
data Image a = Image
    { 
      imageWidth  ::  !Int
      
    , imageHeight ::  !Int
      
      
    , imageData   :: V.Vector (PixelBaseComponent a)
    }
(!!!) :: (Storable e) => V.Vector e -> Int -> e
(!!!) = V.unsafeIndex
class ColorPlane pixel planeToken where
    
    
    toComponentIndex :: pixel -> planeToken -> Int
data PlaneRed = PlaneRed
data PlaneGreen = PlaneGreen
data PlaneBlue = PlaneBlue
data PlaneAlpha = PlaneAlpha
data PlaneLuma = PlaneLuma
data PlaneCr = PlaneCr
data PlaneCb = PlaneCb
data PlaneCyan = PlaneCyan
data PlaneMagenta = PlaneMagenta
data PlaneYellow = PlaneYellow
data PlaneBlack = PlaneBlack
extractComponent :: forall px plane. ( Pixel px
                                     , Pixel (PixelBaseComponent px)
                                     , PixelBaseComponent (PixelBaseComponent px)
                                                    ~ PixelBaseComponent px
                                     , ColorPlane px plane )
                 => plane -> Image px -> Image (PixelBaseComponent px)
extractComponent plane = unsafeExtractComponent idx
    where idx = toComponentIndex (undefined :: px) plane
unsafeExtractComponent :: forall a
                        . ( Pixel a
                          , Pixel (PixelBaseComponent a)
                          , PixelBaseComponent (PixelBaseComponent a)
                                              ~ PixelBaseComponent a)
                       => Int     
                       -> Image a 
                       -> Image (PixelBaseComponent a)
unsafeExtractComponent comp img@(Image { imageWidth = w, imageHeight = h })
  | comp >= padd = error $ "extractComponent : invalid component index ("
                         ++ show comp ++ ", max:" ++ show padd ++ ")"
  | otherwise = Image { imageWidth = w, imageHeight = h, imageData = plane }
      where plane = stride img 1 padd comp
            padd = componentCount (undefined :: a)
dropAlphaLayer :: (TransparentPixel a b) => Image a -> Image b
dropAlphaLayer = pixelMap dropTransparency
class (Pixel a, Pixel b) => TransparentPixel a b | a -> b where
    
    dropTransparency :: a -> b
instance TransparentPixel PixelRGBA8 PixelRGB8 where
    
    dropTransparency (PixelRGBA8 r g b _) = PixelRGB8 r g b
stride :: (Storable (PixelBaseComponent a))
       => Image a -> Int -> Int -> Int -> V.Vector (PixelBaseComponent a)
stride Image { imageWidth = w, imageHeight = h, imageData = array }
        run padd firstComponent = runST $ do
    let cell_count = w * h * run
    outArray <- M.new cell_count
    let strideWrite write_idx _ | write_idx == cell_count = return ()
        strideWrite write_idx read_idx = do
            forM_ [0 .. run  1] $ \i ->
                (outArray .<-. (write_idx + i)) $ array !!! (read_idx + i)
            strideWrite (write_idx + run) (read_idx + padd)
    strideWrite 0 firstComponent
    V.unsafeFreeze outArray
instance NFData (Image a) where
    rnf (Image width height dat) = width  `seq`
                                   height `seq`
                                   dat    `seq`
                                   ()
data MutableImage s a = MutableImage
    { 
      mutableImageWidth  ::  !Int
      
    , mutableImageHeight ::  !Int
      
      
    , mutableImageData   :: M.STVector s (PixelBaseComponent a)
    }
freezeImage :: (Storable (PixelBaseComponent a), PrimMonad m)
            => MutableImage (PrimState m) a -> m (Image a)
freezeImage (MutableImage w h d) = Image w h `liftM` V.freeze d
unsafeFreezeImage ::  (Storable (PixelBaseComponent a), PrimMonad m)
                  => MutableImage (PrimState m) a -> m (Image a)
unsafeFreezeImage (MutableImage w h d) = Image w h `liftM` V.unsafeFreeze d
instance NFData (MutableImage s a) where
    rnf (MutableImage width height dat) = width  `seq`
                                          height `seq`
                                          dat    `seq`
                                          ()
data DynamicImage =
       
       ImageY8    (Image Pixel8)
       
     | ImageY16   (Image Pixel16)
       
     | ImageYF    (Image PixelF)
       
     | ImageYA8   (Image PixelYA8)
      
     | ImageYA16  (Image PixelYA16)
       
     | ImageRGB8  (Image PixelRGB8)
       
     | ImageRGB16 (Image PixelRGB16)
       
     | ImageRGBF  (Image PixelRGBF)
       
     | ImageRGBA8 (Image PixelRGBA8)
       
     | ImageRGBA16 (Image PixelRGBA16)
       
     | ImageYCbCr8 (Image PixelYCbCr8)
       
     | ImageCMYK8  (Image PixelCMYK8)
       
     | ImageCMYK16 (Image PixelCMYK16)
dynamicMap :: (forall pixel . (Pixel pixel) => Image pixel -> a)
           -> DynamicImage -> a
dynamicMap f (ImageY8    i) = f i
dynamicMap f (ImageY16   i) = f i
dynamicMap f (ImageYF    i) = f i
dynamicMap f (ImageYA8   i) = f i
dynamicMap f (ImageYA16  i) = f i
dynamicMap f (ImageRGB8  i) = f i
dynamicMap f (ImageRGB16 i) = f i
dynamicMap f (ImageRGBF  i) = f i
dynamicMap f (ImageRGBA8 i) = f i
dynamicMap f (ImageRGBA16 i) = f i
dynamicMap f (ImageYCbCr8 i) = f i
dynamicMap f (ImageCMYK8 i) = f i
dynamicMap f (ImageCMYK16 i) = f i
dynamicPixelMap :: (forall pixel . (Pixel pixel) => Image pixel -> Image pixel)
                -> DynamicImage -> DynamicImage
dynamicPixelMap f = aux
  where
    aux (ImageY8    i) = ImageY8 (f i)
    aux (ImageY16   i) = ImageY16 (f i)
    aux (ImageYF    i) = ImageYF (f i)
    aux (ImageYA8   i) = ImageYA8 (f i)
    aux (ImageYA16  i) = ImageYA16 (f i)
    aux (ImageRGB8  i) = ImageRGB8 (f i)
    aux (ImageRGB16 i) = ImageRGB16 (f i)
    aux (ImageRGBF  i) = ImageRGBF (f i)
    aux (ImageRGBA8 i) = ImageRGBA8 (f i)
    aux (ImageRGBA16 i) = ImageRGBA16 (f i)
    aux (ImageYCbCr8 i) = ImageYCbCr8 (f i)
    aux (ImageCMYK8 i) = ImageCMYK8 (f i)
    aux (ImageCMYK16 i) = ImageCMYK16 (f i)
instance NFData DynamicImage where
    rnf (ImageY8 img)     = rnf img
    rnf (ImageY16 img)    = rnf img
    rnf (ImageYF img)     = rnf img
    rnf (ImageYA8 img)    = rnf img
    rnf (ImageYA16 img)   = rnf img
    rnf (ImageRGB8 img)   = rnf img
    rnf (ImageRGB16 img)  = rnf img
    rnf (ImageRGBF img)   = rnf img
    rnf (ImageRGBA8 img)  = rnf img
    rnf (ImageRGBA16 img) = rnf img
    rnf (ImageYCbCr8 img) = rnf img
    rnf (ImageCMYK8 img)  = rnf img
    rnf (ImageCMYK16 img)  = rnf img
type Pixel8 = Word8
type Pixel16 = Word16
type PixelF = Float
data PixelYA8 = PixelYA8  !Pixel8  
                          !Pixel8  
              deriving (Eq, Show)
data PixelYA16 = PixelYA16  !Pixel16  
                            !Pixel16  
              deriving (Eq, Show)
data PixelRGB8 = PixelRGB8  !Pixel8 
                            !Pixel8 
                            !Pixel8 
               deriving (Eq, Show)
data PixelRGB16 = PixelRGB16  !Pixel16 
                              !Pixel16 
                              !Pixel16 
               deriving (Eq, Show)
data PixelRGBF = PixelRGBF  !PixelF 
                            !PixelF 
                            !PixelF 
               deriving (Eq, Show)
data PixelYCbCr8 = PixelYCbCr8  !Pixel8 
                                !Pixel8 
                                !Pixel8 
                 deriving (Eq, Show)
data PixelCMYK8 = PixelCMYK8  !Pixel8 
                              !Pixel8 
                              !Pixel8 
                              !Pixel8 
                 deriving (Eq, Show)
data PixelCMYK16 = PixelCMYK16  !Pixel16 
                                !Pixel16 
                                !Pixel16 
                                !Pixel16 
                 deriving (Eq, Show)
data PixelRGBA8 = PixelRGBA8  !Pixel8 
                              !Pixel8 
                              !Pixel8 
                              !Pixel8 
                deriving (Eq, Show)
data PixelRGBA16 = PixelRGBA16  !Pixel16 
                                !Pixel16 
                                !Pixel16 
                                !Pixel16 
                deriving (Eq, Show)
class ( Storable (PixelBaseComponent a)
      , Num (PixelBaseComponent a), Eq a ) => Pixel a where
    
    
    
    type PixelBaseComponent a :: *
    
    
    
    
    
    
    mixWith :: (Int -> PixelBaseComponent a -> PixelBaseComponent a -> PixelBaseComponent a)
            -> a -> a -> a
    
    componentCount :: a -> Int
    
    colorMap :: (PixelBaseComponent a -> PixelBaseComponent a) -> a -> a
    
    pixelBaseIndex :: Image a -> Int -> Int -> Int
    pixelBaseIndex (Image { imageWidth = w }) x y =
            (x + y * w) * componentCount (undefined :: a)
    
    mutablePixelBaseIndex :: MutableImage s a -> Int -> Int -> Int
    mutablePixelBaseIndex (MutableImage { mutableImageWidth = w }) x y =
            (x + y * w) * componentCount (undefined :: a)
    
    
    
    pixelAt :: Image a -> Int -> Int -> a
    
    readPixel :: PrimMonad m => MutableImage (PrimState m) a -> Int -> Int -> m a
    
    writePixel :: PrimMonad m => MutableImage (PrimState m) a -> Int -> Int -> a -> m ()
    
    
    
    unsafePixelAt :: V.Vector (PixelBaseComponent a) -> Int -> a
    
    
    
    unsafeReadPixel :: PrimMonad m => M.STVector (PrimState m) (PixelBaseComponent a) -> Int -> m a
    
    
    
    unsafeWritePixel :: PrimMonad m => M.STVector (PrimState m) (PixelBaseComponent a) -> Int -> a -> m ()
class (Pixel a, Pixel b) => ColorConvertible a b where
    
    
    promotePixel :: a -> b
    
    
    promoteImage :: Image a -> Image b
    promoteImage = pixelMap promotePixel
class (Pixel a, Pixel b) => ColorSpaceConvertible a b where
    
    
    convertPixel :: a -> b
    
    
    convertImage :: Image a -> Image b
    convertImage = pixelMap convertPixel
generateImage :: forall a. (Pixel a)
              => (Int -> Int -> a)  
              -> Int        
              -> Int        
              -> Image a
generateImage f w h = Image { imageWidth = w, imageHeight = h, imageData = generated }
  where compCount = componentCount (undefined :: a)
        generated = runST $ do
            arr <- M.new (w * h * compCount)
            let lineGenerator _ y | y >= h = return ()
                lineGenerator lineIdx y = column lineIdx 0
                  where column idx x | x >= w = lineGenerator idx $ y + 1
                        column idx x = do
                            unsafeWritePixel arr idx $ f x y
                            column (idx + compCount) $ x + 1
            lineGenerator 0 0
            V.unsafeFreeze arr
withImage :: forall m pixel. (Pixel pixel, PrimMonad m)
          => Int                     
          -> Int                     
          -> (Int -> Int -> m pixel) 
          -> m (Image pixel)
withImage width height pixelGenerator = do
  let pixelComponentCount = componentCount (undefined :: pixel)
  arr <- M.new (width * height * pixelComponentCount)
  let mutImage = MutableImage
        { mutableImageWidth = width
        , mutableImageHeight = height
        , mutableImageData = arr
        }
  let pixelPositions = [(x, y) | y <- [0 .. height1], x <- [0..width1]]
  sequence_ [pixelGenerator x y >>= unsafeWritePixel arr idx
                        | ((x,y), idx) <- zip pixelPositions [0, pixelComponentCount ..]]
  unsafeFreezeImage mutImage
generateFoldImage :: forall a acc. (Pixel a)
                  => (acc -> Int -> Int -> (acc, a)) 
                  -> acc        
                  -> Int        
                  -> Int        
                  -> (acc, Image a)
generateFoldImage f intialAcc w h =
 (finalState, Image { imageWidth = w, imageHeight = h, imageData = generated })
  where compCount = componentCount (undefined :: a)
        (finalState, generated) = runST $ do
            arr <- M.new (w * h * compCount)
            let mutImage = MutableImage {
                                mutableImageWidth = w,
                                mutableImageHeight = h,
                                mutableImageData = arr }
            foldResult <- foldM (\acc (x,y) -> do
                    let (acc', px) = f acc x y
                    writePixel mutImage x y px
                    return acc') intialAcc [(x,y) | y <- [0 .. h1], x <- [0 .. w1]]
            frozen <- V.unsafeFreeze arr
            return (foldResult, frozen)
pixelFold :: (Pixel pixel)
          => (acc -> Int -> Int -> pixel -> acc) -> acc -> Image pixel -> acc
pixelFold f initialAccumulator img@(Image { imageWidth = w, imageHeight = h }) =
  lineFold
    where pixelFolder y acc x = f acc x y $ pixelAt img x y
          columnFold lineAcc y = foldl' (pixelFolder y) lineAcc [0 .. w  1]
          lineFold = foldl' columnFold initialAccumulator [0 .. h  1]
pixelMap :: forall a b. (Pixel a, Pixel b)
         => (a -> b) -> Image a -> Image b
pixelMap f Image { imageWidth = w, imageHeight = h, imageData = vec } =
  Image w h pixels
    where sourceComponentCount = componentCount (undefined :: a)
          destComponentCount = componentCount (undefined :: b)
          pixels = runST $ do
            newArr <- M.new (w * h * destComponentCount)
            let lineMapper _ _ y | y >= h = return ()
                lineMapper readIdxLine writeIdxLine y = colMapper readIdxLine writeIdxLine 0
                  where colMapper readIdx writeIdx x
                            | x >= w = lineMapper readIdx writeIdx $ y + 1
                            | otherwise = do
                                unsafeWritePixel newArr writeIdx . f $ unsafePixelAt vec readIdx
                                colMapper (readIdx + sourceComponentCount)
                                          (writeIdx + destComponentCount)
                                          (x + 1)
            lineMapper 0 0 0
            
            
            
            V.unsafeFreeze newArr
class (Pixel a, Pixel (PixelBaseComponent a)) => LumaPlaneExtractable a where
    
    computeLuma      :: a -> (PixelBaseComponent a)
    
    
    
    
    
    
    extractLumaPlane :: Image a -> Image (PixelBaseComponent a)
    extractLumaPlane = pixelMap computeLuma
instance LumaPlaneExtractable Pixel8 where
    
    computeLuma = id
    extractLumaPlane = id
instance LumaPlaneExtractable Pixel16 where
    
    computeLuma = id
    extractLumaPlane = id
instance LumaPlaneExtractable PixelF where
    
    computeLuma = id
    extractLumaPlane = id
instance LumaPlaneExtractable PixelRGBF where
    
    computeLuma (PixelRGBF r g b) =
        0.3 * r + 0.59 * g + 0.11 * b
instance LumaPlaneExtractable PixelRGBA8 where
    
    computeLuma (PixelRGBA8 r g b _) = floor $ 0.3 * toRational r +
                                             0.59 * toRational g +
                                             0.11 * toRational b
instance LumaPlaneExtractable PixelYCbCr8 where
    
    computeLuma (PixelYCbCr8 y _ _) = y
    extractLumaPlane = extractComponent PlaneLuma
instance (Pixel a) => ColorConvertible a a where
    
    promotePixel = id
    
    promoteImage = id
(.!!!.) :: (PrimMonad m, Storable a) => M.STVector (PrimState m) a -> Int -> m a
(.!!!.) = M.read 
(.<-.) :: (PrimMonad m, Storable a) => M.STVector (PrimState m) a -> Int -> a -> m ()
(.<-.)  = M.write 
instance Pixel Pixel8 where
    type PixelBaseComponent Pixel8 = Word8
    
    mixWith f = f 0
    
    colorMap f = f
    componentCount _ = 1
    pixelAt (Image { imageWidth = w, imageData = arr }) x y = arr ! (x + y * w)
    readPixel image@(MutableImage { mutableImageData = arr }) x y =
        arr `M.read` mutablePixelBaseIndex image x y
    writePixel image@(MutableImage { mutableImageData = arr }) x y =
        arr `M.write` mutablePixelBaseIndex image x y
    unsafePixelAt = V.unsafeIndex
    unsafeReadPixel = M.unsafeRead
    unsafeWritePixel = M.unsafeWrite
instance ColorConvertible Pixel8 PixelYA8 where
    
    promotePixel c = PixelYA8 c 255
instance ColorConvertible Pixel8 PixelF where
    
    promotePixel c = fromIntegral c / 255.0
instance ColorConvertible Pixel8 Pixel16 where
    
    promotePixel c = fromIntegral c `unsafeShiftL` 8
instance ColorConvertible Pixel8 PixelRGB8 where
    
    promotePixel c = PixelRGB8 c c c
instance ColorConvertible Pixel8 PixelRGBA8 where
    
    promotePixel c = PixelRGBA8 c c c 255
instance Pixel Pixel16 where
    type PixelBaseComponent Pixel16 = Word16
    
    mixWith f = f 0
    
    colorMap f = f
    componentCount _ = 1
    pixelAt (Image { imageWidth = w, imageData = arr }) x y = arr ! (x + y * w)
    readPixel image@(MutableImage { mutableImageData = arr }) x y =
        arr `M.read` mutablePixelBaseIndex image x y
    writePixel image@(MutableImage { mutableImageData = arr }) x y =
        arr `M.write` mutablePixelBaseIndex image x y
    unsafePixelAt = V.unsafeIndex
    unsafeReadPixel = M.unsafeRead
    unsafeWritePixel = M.unsafeWrite
instance ColorConvertible Pixel16 PixelYA16 where
    
    promotePixel c = PixelYA16 c maxBound
instance ColorConvertible Pixel16 PixelRGB16 where
    
    promotePixel c = PixelRGB16 c c c
instance ColorConvertible Pixel16 PixelRGBA16 where
    
    promotePixel c = PixelRGBA16 c c c maxBound
instance Pixel PixelF where
    type PixelBaseComponent PixelF = Float
    
    mixWith f = f 0
    
    colorMap f = f
    componentCount _ = 1
    pixelAt (Image { imageWidth = w, imageData = arr }) x y = arr ! (x + y * w)
    readPixel image@(MutableImage { mutableImageData = arr }) x y =
        arr `M.read` mutablePixelBaseIndex image x y
    writePixel image@(MutableImage { mutableImageData = arr }) x y =
        arr `M.write` mutablePixelBaseIndex image x y
    unsafePixelAt = V.unsafeIndex
    unsafeReadPixel = M.unsafeRead
    unsafeWritePixel = M.unsafeWrite
instance ColorConvertible PixelF PixelRGBF where
    
    promotePixel c = PixelRGBF c c c
instance Pixel PixelYA8 where
    type PixelBaseComponent PixelYA8 = Word8
    
    mixWith f (PixelYA8 ya aa) (PixelYA8 yb ab) =
        PixelYA8 (f 0 ya yb) (f 1 aa ab)
    
    colorMap f (PixelYA8 y a) = PixelYA8 (f y) (f a)
    componentCount _ = 2
    pixelAt image@(Image { imageData = arr }) x y = PixelYA8 (arr ! (baseIdx + 0))
                                                             (arr ! (baseIdx + 1))
        where baseIdx = pixelBaseIndex image x y
    readPixel image@(MutableImage { mutableImageData = arr }) x y = do
        yv <- arr .!!!. baseIdx
        av <- arr .!!!. (baseIdx + 1)
        return $ PixelYA8 yv av
        where baseIdx = mutablePixelBaseIndex image x y
    writePixel image@(MutableImage { mutableImageData = arr }) x y (PixelYA8 yv av) = do
        let baseIdx = mutablePixelBaseIndex image x y
        (arr .<-. (baseIdx + 0)) yv
        (arr .<-. (baseIdx + 1)) av
    unsafePixelAt v idx =
        PixelYA8 (V.unsafeIndex v idx) (V.unsafeIndex v $ idx + 1)
    unsafeReadPixel vec idx =
        PixelYA8 `liftM` M.unsafeRead vec idx `ap` M.unsafeRead vec (idx + 1)
    unsafeWritePixel v idx (PixelYA8 y a) =
        M.unsafeWrite v idx y >> M.unsafeWrite v (idx + 1) a
instance ColorConvertible PixelYA8 PixelRGB8 where
    
    promotePixel (PixelYA8 y _) = PixelRGB8 y y y
instance ColorConvertible PixelYA8 PixelRGBA8 where
    
    promotePixel (PixelYA8 y a) = PixelRGBA8 y y y a
instance ColorPlane PixelYA8 PlaneLuma where
    toComponentIndex _ _ = 0
instance ColorPlane PixelYA8 PlaneAlpha where
    toComponentIndex _ _ = 1
instance TransparentPixel PixelYA8 Pixel8 where
    
    dropTransparency (PixelYA8 y _) = y
instance LumaPlaneExtractable PixelYA8 where
    
    computeLuma (PixelYA8 y _) = y
    extractLumaPlane = extractComponent PlaneLuma
instance Pixel PixelYA16 where
    type PixelBaseComponent PixelYA16 = Word16
    
    mixWith f (PixelYA16 ya aa) (PixelYA16 yb ab) =
        PixelYA16 (f 0 ya yb) (f 1 aa ab)
    
    colorMap f (PixelYA16 y a) = PixelYA16 (f y) (f a)
    componentCount _ = 2
    pixelAt image@(Image { imageData = arr }) x y = PixelYA16 (arr ! (baseIdx + 0))
                                                              (arr ! (baseIdx + 1))
        where baseIdx = pixelBaseIndex image x y
    readPixel image@(MutableImage { mutableImageData = arr }) x y = do
        yv <- arr .!!!. baseIdx
        av <- arr .!!!. (baseIdx + 1)
        return $ PixelYA16 yv av
        where baseIdx = mutablePixelBaseIndex image x y
    writePixel image@(MutableImage { mutableImageData = arr }) x y (PixelYA16 yv av) = do
        let baseIdx = mutablePixelBaseIndex image x y
        (arr .<-. (baseIdx + 0)) yv
        (arr .<-. (baseIdx + 1)) av
    unsafePixelAt v idx =
        PixelYA16 (V.unsafeIndex v idx) (V.unsafeIndex v $ idx + 1)
    unsafeReadPixel vec idx =
        PixelYA16 `liftM` M.unsafeRead vec idx `ap` M.unsafeRead vec (idx + 1)
    unsafeWritePixel v idx (PixelYA16 y a) =
        M.unsafeWrite v idx y >> M.unsafeWrite v (idx + 1) a
instance ColorConvertible PixelYA16 PixelRGBA16 where
    
    promotePixel (PixelYA16 y a) = PixelRGBA16 y y y a
instance ColorPlane PixelYA16 PlaneLuma where
    toComponentIndex _ _ = 0
instance ColorPlane PixelYA16 PlaneAlpha where
    toComponentIndex _ _ = 1
instance TransparentPixel PixelYA16 Pixel16 where
    
    dropTransparency (PixelYA16 y _) = y
instance Pixel PixelRGBF where
    type PixelBaseComponent PixelRGBF = PixelF
    
    mixWith f (PixelRGBF ra ga ba) (PixelRGBF rb gb bb) =
        PixelRGBF (f 0 ra rb) (f 1 ga gb) (f 2 ba bb)
    
    colorMap f (PixelRGBF r g b) = PixelRGBF (f r) (f g) (f b)
    componentCount _ = 3
    pixelAt image@(Image { imageData = arr }) x y = PixelRGBF (arr ! (baseIdx + 0))
                                                              (arr ! (baseIdx + 1))
                                                              (arr ! (baseIdx + 2))
        where baseIdx = pixelBaseIndex image x y
    readPixel image@(MutableImage { mutableImageData = arr }) x y = do
        rv <- arr .!!!. baseIdx
        gv <- arr .!!!. (baseIdx + 1)
        bv <- arr .!!!. (baseIdx + 2)
        return $ PixelRGBF rv gv bv
        where baseIdx = mutablePixelBaseIndex image x y
    writePixel image@(MutableImage { mutableImageData = arr }) x y (PixelRGBF rv gv bv) = do
        let baseIdx = mutablePixelBaseIndex image x y
        (arr .<-. (baseIdx + 0)) rv
        (arr .<-. (baseIdx + 1)) gv
        (arr .<-. (baseIdx + 2)) bv
    unsafePixelAt v idx =
        PixelRGBF (V.unsafeIndex v idx) (V.unsafeIndex v $ idx + 1) (V.unsafeIndex v $ idx + 2)
    unsafeReadPixel vec idx =
        PixelRGBF `liftM` M.unsafeRead vec idx
                  `ap` M.unsafeRead vec (idx + 1)
                  `ap` M.unsafeRead vec (idx + 2)
    unsafeWritePixel v idx (PixelRGBF r g b) =
        M.unsafeWrite v idx r >> M.unsafeWrite v (idx + 1) g
                              >> M.unsafeWrite v (idx + 2) b
instance ColorPlane PixelRGBF PlaneRed where
    toComponentIndex _ _ = 0
instance ColorPlane PixelRGBF PlaneGreen where
    toComponentIndex _ _ = 1
instance ColorPlane PixelRGBF PlaneBlue where
    toComponentIndex _ _ = 2
instance Pixel PixelRGB16 where
    type PixelBaseComponent PixelRGB16 = Pixel16
    
    mixWith f (PixelRGB16 ra ga ba) (PixelRGB16 rb gb bb) =
        PixelRGB16 (f 0 ra rb) (f 1 ga gb) (f 2 ba bb)
    
    colorMap f (PixelRGB16 r g b) = PixelRGB16 (f r) (f g) (f b)
    componentCount _ = 3
    pixelAt image@(Image { imageData = arr }) x y = PixelRGB16 (arr ! (baseIdx + 0))
                                                               (arr ! (baseIdx + 1))
                                                               (arr ! (baseIdx + 2))
        where baseIdx = pixelBaseIndex image x y
    readPixel image@(MutableImage { mutableImageData = arr }) x y = do
        rv <- arr .!!!. baseIdx
        gv <- arr .!!!. (baseIdx + 1)
        bv <- arr .!!!. (baseIdx + 2)
        return $ PixelRGB16 rv gv bv
        where baseIdx = mutablePixelBaseIndex image x y
    writePixel image@(MutableImage { mutableImageData = arr }) x y (PixelRGB16 rv gv bv) = do
        let baseIdx = mutablePixelBaseIndex image x y
        (arr .<-. (baseIdx + 0)) rv
        (arr .<-. (baseIdx + 1)) gv
        (arr .<-. (baseIdx + 2)) bv
    unsafePixelAt v idx =
        PixelRGB16 (V.unsafeIndex v idx) (V.unsafeIndex v $ idx + 1) (V.unsafeIndex v $ idx + 2)
    unsafeReadPixel vec idx =
        PixelRGB16 `liftM` M.unsafeRead vec idx
                   `ap` M.unsafeRead vec (idx + 1)
                   `ap` M.unsafeRead vec (idx + 2)
    unsafeWritePixel v idx (PixelRGB16 r g b) =
        M.unsafeWrite v idx r >> M.unsafeWrite v (idx + 1) g
                              >> M.unsafeWrite v (idx + 2) b
instance ColorPlane PixelRGB16 PlaneRed where
    toComponentIndex _ _ = 0
instance ColorPlane PixelRGB16 PlaneGreen where
    toComponentIndex _ _ = 1
instance ColorPlane PixelRGB16 PlaneBlue where
    toComponentIndex _ _ = 2
instance ColorSpaceConvertible PixelRGB16 PixelCMYK16 where
    
    convertPixel (PixelRGB16 r g b) = integralRGBToCMYK PixelCMYK16 (r, g, b)
instance ColorConvertible PixelRGB16 PixelRGBA16 where
    
    promotePixel (PixelRGB16 r g b) = PixelRGBA16 r g b maxBound
instance LumaPlaneExtractable PixelRGB16 where
    
    computeLuma (PixelRGB16 r g b) = floor $ 0.3 * toRational r +
                                             0.59 * toRational g +
                                             0.11 * toRational b
instance Pixel PixelRGB8 where
    type PixelBaseComponent PixelRGB8 = Word8
    
    mixWith f (PixelRGB8 ra ga ba) (PixelRGB8 rb gb bb) =
        PixelRGB8 (f 0 ra rb) (f 1 ga gb) (f 2 ba bb)
    
    colorMap f (PixelRGB8 r g b) = PixelRGB8 (f r) (f g) (f b)
    componentCount _ = 3
    pixelAt image@(Image { imageData = arr }) x y = PixelRGB8 (arr ! (baseIdx + 0))
                                                              (arr ! (baseIdx + 1))
                                                              (arr ! (baseIdx + 2))
        where baseIdx = pixelBaseIndex image x y
    readPixel image@(MutableImage { mutableImageData = arr }) x y = do
        rv <- arr .!!!. baseIdx
        gv <- arr .!!!. (baseIdx + 1)
        bv <- arr .!!!. (baseIdx + 2)
        return $ PixelRGB8 rv gv bv
        where baseIdx = mutablePixelBaseIndex image x y
    writePixel image@(MutableImage { mutableImageData = arr }) x y (PixelRGB8 rv gv bv) = do
        let baseIdx = mutablePixelBaseIndex image x y
        (arr .<-. (baseIdx + 0)) rv
        (arr .<-. (baseIdx + 1)) gv
        (arr .<-. (baseIdx + 2)) bv
    unsafePixelAt v idx =
        PixelRGB8 (V.unsafeIndex v idx) (V.unsafeIndex v $ idx + 1) (V.unsafeIndex v $ idx + 2)
    unsafeReadPixel vec idx =
        PixelRGB8 `liftM` M.unsafeRead vec idx
                  `ap` M.unsafeRead vec (idx + 1)
                  `ap` M.unsafeRead vec (idx + 2)
    unsafeWritePixel v idx (PixelRGB8 r g b) =
        M.unsafeWrite v idx r >> M.unsafeWrite v (idx + 1) g
                              >> M.unsafeWrite v (idx + 2) b
instance ColorConvertible PixelRGB8 PixelRGBA8 where
    
    promotePixel (PixelRGB8 r g b) = PixelRGBA8 r g b maxBound
instance ColorConvertible PixelRGB8 PixelRGBF where
    
    promotePixel (PixelRGB8 r g b) = PixelRGBF (toF r) (toF g) (toF b)
        where toF v = fromIntegral v / 255.0
instance ColorPlane PixelRGB8 PlaneRed where
    toComponentIndex _ _ = 0
instance ColorPlane PixelRGB8 PlaneGreen where
    toComponentIndex _ _ = 1
instance ColorPlane PixelRGB8 PlaneBlue where
    toComponentIndex _ _ = 2
instance LumaPlaneExtractable PixelRGB8 where
    
    computeLuma (PixelRGB8 r g b) = floor $ 0.3 * toRational r +
                                            0.59 * toRational g +
                                            0.11 * toRational b
instance Pixel PixelRGBA8 where
    type PixelBaseComponent PixelRGBA8 = Word8
    
    mixWith f (PixelRGBA8 ra ga ba aa) (PixelRGBA8 rb gb bb ab) =
        PixelRGBA8 (f 0 ra rb) (f 1 ga gb) (f 2 ba bb) (f 3 aa ab)
    
    colorMap f (PixelRGBA8 r g b a) = PixelRGBA8 (f r) (f g) (f b) (f a)
    componentCount _ = 4
    pixelAt image@(Image { imageData = arr }) x y = PixelRGBA8 (arr ! (baseIdx + 0))
                                                               (arr ! (baseIdx + 1))
                                                               (arr ! (baseIdx + 2))
                                                               (arr ! (baseIdx + 3))
        where baseIdx = pixelBaseIndex image x y
    readPixel image@(MutableImage { mutableImageData = arr }) x y = do
        rv <- arr .!!!. baseIdx
        gv <- arr .!!!. (baseIdx + 1)
        bv <- arr .!!!. (baseIdx + 2)
        av <- arr .!!!. (baseIdx + 3)
        return $ PixelRGBA8 rv gv bv av
        where baseIdx = mutablePixelBaseIndex image x y
    writePixel image@(MutableImage { mutableImageData = arr }) x y (PixelRGBA8 rv gv bv av) = do
        let baseIdx = mutablePixelBaseIndex image x y
        (arr .<-. (baseIdx + 0)) rv
        (arr .<-. (baseIdx + 1)) gv
        (arr .<-. (baseIdx + 2)) bv
        (arr .<-. (baseIdx + 3)) av
    unsafePixelAt v idx =
        PixelRGBA8 (V.unsafeIndex v idx)
                   (V.unsafeIndex v $ idx + 1)
                   (V.unsafeIndex v $ idx + 2)
                   (V.unsafeIndex v $ idx + 3)
    unsafeReadPixel vec idx =
        PixelRGBA8 `liftM` M.unsafeRead vec idx
                   `ap` M.unsafeRead vec (idx + 1)
                   `ap` M.unsafeRead vec (idx + 2)
                   `ap` M.unsafeRead vec (idx + 3)
    unsafeWritePixel v idx (PixelRGBA8 r g b a) =
        M.unsafeWrite v idx r >> M.unsafeWrite v (idx + 1) g
                              >> M.unsafeWrite v (idx + 2) b
                              >> M.unsafeWrite v (idx + 3) a
instance ColorPlane PixelRGBA8 PlaneRed where
    toComponentIndex _ _ = 0
instance ColorPlane PixelRGBA8 PlaneGreen where
    toComponentIndex _ _ = 1
instance ColorPlane PixelRGBA8 PlaneBlue where
    toComponentIndex _ _ = 2
instance ColorPlane PixelRGBA8 PlaneAlpha where
    toComponentIndex _ _ = 3
instance Pixel PixelRGBA16 where
    type PixelBaseComponent PixelRGBA16 = Pixel16
    
    mixWith f (PixelRGBA16 ra ga ba aa) (PixelRGBA16 rb gb bb ab) =
        PixelRGBA16 (f 0 ra rb) (f 1 ga gb) (f 2 ba bb) (f 3 aa ab)
    
    colorMap f (PixelRGBA16 r g b a) = PixelRGBA16 (f r) (f g) (f b) (f a)
    componentCount _ = 4
    pixelAt image@(Image { imageData = arr }) x y =
                PixelRGBA16 (arr ! (baseIdx + 0)) (arr ! (baseIdx + 1))
                            (arr ! (baseIdx + 2)) (arr ! (baseIdx + 3))
        where baseIdx = pixelBaseIndex image x y
    readPixel image@(MutableImage { mutableImageData = arr }) x y = do
        rv <- arr .!!!. baseIdx
        gv <- arr .!!!. (baseIdx + 1)
        bv <- arr .!!!. (baseIdx + 2)
        av <- arr .!!!. (baseIdx + 3)
        return $ PixelRGBA16 rv gv bv av
        where baseIdx = mutablePixelBaseIndex image x y
    writePixel image@(MutableImage { mutableImageData = arr }) x y (PixelRGBA16 rv gv bv av) = do
        let baseIdx = mutablePixelBaseIndex image x y
        (arr .<-. (baseIdx + 0)) rv
        (arr .<-. (baseIdx + 1)) gv
        (arr .<-. (baseIdx + 2)) bv
        (arr .<-. (baseIdx + 3)) av
    unsafePixelAt v idx =
        PixelRGBA16 (V.unsafeIndex v idx)
                    (V.unsafeIndex v $ idx + 1)
                    (V.unsafeIndex v $ idx + 2)
                    (V.unsafeIndex v $ idx + 3)
    unsafeReadPixel vec idx =
        PixelRGBA16 `liftM` M.unsafeRead vec idx
                    `ap` M.unsafeRead vec (idx + 1)
                    `ap` M.unsafeRead vec (idx + 2)
                    `ap` M.unsafeRead vec (idx + 3)
    unsafeWritePixel v idx (PixelRGBA16 r g b a) =
        M.unsafeWrite v idx r >> M.unsafeWrite v (idx + 1) g
                              >> M.unsafeWrite v (idx + 2) b
                              >> M.unsafeWrite v (idx + 3) a
instance TransparentPixel PixelRGBA16 PixelRGB16 where
    
    dropTransparency (PixelRGBA16 r g b _) = PixelRGB16 r g b
instance ColorPlane PixelRGBA16 PlaneRed where
    toComponentIndex _ _ = 0
instance ColorPlane PixelRGBA16 PlaneGreen where
    toComponentIndex _ _ = 1
instance ColorPlane PixelRGBA16 PlaneBlue where
    toComponentIndex _ _ = 2
instance ColorPlane PixelRGBA16 PlaneAlpha where
    toComponentIndex _ _ = 3
instance Pixel PixelYCbCr8 where
    type PixelBaseComponent PixelYCbCr8 = Word8
    
    mixWith f (PixelYCbCr8 ya cba cra) (PixelYCbCr8 yb cbb crb) =
        PixelYCbCr8 (f 0 ya yb) (f 1 cba cbb) (f 2 cra crb)
    
    colorMap f (PixelYCbCr8 y cb cr) = PixelYCbCr8 (f y) (f cb) (f cr)
    componentCount _ = 3
    pixelAt image@(Image { imageData = arr }) x y = PixelYCbCr8 (arr ! (baseIdx + 0))
                                                                (arr ! (baseIdx + 1))
                                                                (arr ! (baseIdx + 2))
        where baseIdx = pixelBaseIndex image x y
    readPixel image@(MutableImage { mutableImageData = arr }) x y = do
        yv <- arr .!!!. baseIdx
        cbv <- arr .!!!. (baseIdx + 1)
        crv <- arr .!!!. (baseIdx + 2)
        return $ PixelYCbCr8 yv cbv crv
        where baseIdx = mutablePixelBaseIndex image x y
    writePixel image@(MutableImage { mutableImageData = arr }) x y (PixelYCbCr8 yv cbv crv) = do
        let baseIdx = mutablePixelBaseIndex image x y
        (arr .<-. (baseIdx + 0)) yv
        (arr .<-. (baseIdx + 1)) cbv
        (arr .<-. (baseIdx + 2)) crv
    unsafePixelAt v idx =
        PixelYCbCr8 (V.unsafeIndex v idx) (V.unsafeIndex v $ idx + 1) (V.unsafeIndex v $ idx + 2)
    unsafeReadPixel vec idx =
        PixelYCbCr8 `liftM` M.unsafeRead vec idx
                    `ap` M.unsafeRead vec (idx + 1)
                    `ap` M.unsafeRead vec (idx + 2)
    unsafeWritePixel v idx (PixelYCbCr8 y cb cr) =
        M.unsafeWrite v idx y >> M.unsafeWrite v (idx + 1) cb
                              >> M.unsafeWrite v (idx + 2) cr
instance (Pixel a) => ColorSpaceConvertible a a where
    convertPixel = id
    convertImage = id
scaleBits, oneHalf :: Int
scaleBits = 16
oneHalf = 1 `unsafeShiftL` (scaleBits  1)
fix :: Float -> Int
fix x = floor $ x * fromIntegral ((1 :: Int) `unsafeShiftL` scaleBits) + 0.5
rYTab, gYTab, bYTab, rCbTab, gCbTab, bCbTab, gCrTab, bCrTab :: V.Vector Int
rYTab = V.fromListN 256 [fix 0.29900 * i | i <- [0..255] ]
gYTab = V.fromListN 256 [fix 0.58700 * i | i <- [0..255] ]
bYTab = V.fromListN 256 [fix 0.11400 * i + oneHalf | i <- [0..255] ]
rCbTab = V.fromListN 256 [( fix 0.16874) * i | i <- [0..255] ]
gCbTab = V.fromListN 256 [( fix 0.33126) * i | i <- [0..255] ]
bCbTab = V.fromListN 256 [fix 0.5 * i + (128 `unsafeShiftL` scaleBits) + oneHalf  1| i <- [0..255] ]
gCrTab = V.fromListN 256 [( fix 0.41869) * i | i <- [0..255] ]
bCrTab = V.fromListN 256 [( fix 0.08131) * i | i <- [0..255] ]
instance ColorSpaceConvertible PixelRGB8 PixelYCbCr8 where
    
    convertPixel (PixelRGB8 r g b) = PixelYCbCr8 (fromIntegral y) (fromIntegral cb) (fromIntegral cr)
      where ri = fromIntegral r
            gi = fromIntegral g
            bi = fromIntegral b
            y  = (rYTab `V.unsafeIndex` ri + gYTab `V.unsafeIndex` gi + bYTab `V.unsafeIndex` bi) `unsafeShiftR` scaleBits
            cb = (rCbTab `V.unsafeIndex` ri + gCbTab `V.unsafeIndex` gi + bCbTab `V.unsafeIndex` bi) `unsafeShiftR` scaleBits
            cr = (bCbTab `V.unsafeIndex` ri + gCrTab `V.unsafeIndex` gi + bCrTab `V.unsafeIndex` bi) `unsafeShiftR` scaleBits
    convertImage Image { imageWidth = w, imageHeight = h, imageData = d } = Image w h newData
        where maxi = w * h
              rY  = fix 0.29900
              gY  = fix 0.58700
              bY  = fix 0.11400
              rCb = ( fix 0.16874)
              gCb = ( fix 0.33126)
              bCb = fix 0.5
              gCr = ( fix 0.41869)
              bCr = ( fix 0.08131)
              newData = runST $ do
                block <- M.new $ maxi * 3
                let traductor _ idx | idx >= maxi = return block
                    traductor readIdx idx = do
                        let ri = fromIntegral $ d `V.unsafeIndex` readIdx
                            gi = fromIntegral $ d `V.unsafeIndex` (readIdx + 1)
                            bi = fromIntegral $ d `V.unsafeIndex` (readIdx + 2)
                            y  = (rY * ri + gY * gi + bY * bi + oneHalf) `unsafeShiftR` scaleBits
                            cb = (rCb * ri + gCb * gi + bCb * bi + (128 `unsafeShiftL` scaleBits) + oneHalf  1) `unsafeShiftR` scaleBits
                            cr = (bCb * ri + (128 `unsafeShiftL` scaleBits) + oneHalf  1+ gCr * gi + bCr * bi) `unsafeShiftR` scaleBits
                        (block `M.unsafeWrite` (readIdx + 0)) $ fromIntegral y
                        (block `M.unsafeWrite` (readIdx + 1)) $ fromIntegral cb
                        (block `M.unsafeWrite` (readIdx + 2)) $ fromIntegral cr
                        traductor (readIdx + 3) (idx + 1)
                traductor 0 0 >>= V.freeze
crRTab, cbBTab, crGTab, cbGTab :: V.Vector Int
crRTab = V.fromListN 256 [(fix 1.40200 * x + oneHalf) `unsafeShiftR` scaleBits | x <- [128 .. 127]]
cbBTab = V.fromListN 256 [(fix 1.77200 * x + oneHalf) `unsafeShiftR` scaleBits | x <- [128 .. 127]]
crGTab = V.fromListN 256 [negate (fix 0.71414) * x | x <- [128 .. 127]]
cbGTab = V.fromListN 256 [negate (fix 0.34414) * x + oneHalf | x <- [128 .. 127]]
instance ColorSpaceConvertible PixelYCbCr8 PixelRGB8 where
    
    convertPixel (PixelYCbCr8 y cb cr) = PixelRGB8 (clampWord8 r) (clampWord8 g) (clampWord8 b)
        where clampWord8 = fromIntegral . max 0 . min 255
              yi = fromIntegral y
              cbi = fromIntegral cb
              cri = fromIntegral cr
              r = yi +  crRTab `V.unsafeIndex` cri
              g = yi + (cbGTab `V.unsafeIndex` cbi + crGTab `V.unsafeIndex` cri) `unsafeShiftR` scaleBits
              b = yi +  cbBTab `V.unsafeIndex` cbi
    convertImage Image { imageWidth = w, imageHeight = h, imageData = d } = Image w h newData
        where maxi = w * h
              clampWord8 v | v < 0 = 0
                           | v > 255 = 255
                           | otherwise = fromIntegral v
              newData = runST $ do
                block <- M.new $ maxi * 3
                let traductor _ idx | idx >= maxi = return block
                    traductor readIdx idx = do
                        let yi =  fromIntegral $ d `V.unsafeIndex` readIdx
                            cbi = fromIntegral $ d `V.unsafeIndex` (readIdx + 1)
                            cri = fromIntegral $ d `V.unsafeIndex` (readIdx + 2)
                            r = yi +  crRTab `V.unsafeIndex` cri
                            g = yi + (cbGTab `V.unsafeIndex` cbi + crGTab `V.unsafeIndex` cri) `unsafeShiftR` scaleBits
                            b = yi +  cbBTab `V.unsafeIndex` cbi
                        (block `M.unsafeWrite` (readIdx + 0)) $ clampWord8 r
                        (block `M.unsafeWrite` (readIdx + 1)) $ clampWord8 g
                        (block `M.unsafeWrite` (readIdx + 2)) $ clampWord8 b
                        traductor (readIdx + 3) (idx + 1)
                traductor 0 0 >>= V.freeze
instance ColorPlane PixelYCbCr8 PlaneLuma where
    toComponentIndex _ _ = 0
instance ColorPlane PixelYCbCr8 PlaneCb where
    toComponentIndex _ _ = 1
instance ColorPlane PixelYCbCr8 PlaneCr where
    toComponentIndex _ _ = 2
instance Pixel PixelCMYK8 where
    type PixelBaseComponent PixelCMYK8 = Word8
    
    mixWith f (PixelCMYK8 ca ma ya ka) (PixelCMYK8 cb mb yb kb) =
        PixelCMYK8 (f 0 ca cb) (f 1 ma mb) (f 2 ya yb) (f 3 ka kb)
    
    colorMap f (PixelCMYK8 c m y k) = PixelCMYK8 (f c) (f m) (f y) (f k)
    componentCount _ = 4
    pixelAt image@(Image { imageData = arr }) x y = PixelCMYK8 (arr ! (baseIdx + 0))
                                                               (arr ! (baseIdx + 1))
                                                               (arr ! (baseIdx + 2))
                                                               (arr ! (baseIdx + 3))
        where baseIdx = pixelBaseIndex image x y
    readPixel image@(MutableImage { mutableImageData = arr }) x y = do
        rv <- arr .!!!. baseIdx
        gv <- arr .!!!. (baseIdx + 1)
        bv <- arr .!!!. (baseIdx + 2)
        av <- arr .!!!. (baseIdx + 3)
        return $ PixelCMYK8 rv gv bv av
        where baseIdx = mutablePixelBaseIndex image x y
    writePixel image@(MutableImage { mutableImageData = arr }) x y (PixelCMYK8 rv gv bv av) = do
        let baseIdx = mutablePixelBaseIndex image x y
        (arr .<-. (baseIdx + 0)) rv
        (arr .<-. (baseIdx + 1)) gv
        (arr .<-. (baseIdx + 2)) bv
        (arr .<-. (baseIdx + 3)) av
    unsafePixelAt v idx =
        PixelCMYK8 (V.unsafeIndex v idx)
                   (V.unsafeIndex v $ idx + 1)
                   (V.unsafeIndex v $ idx + 2)
                   (V.unsafeIndex v $ idx + 3)
    unsafeReadPixel vec idx =
        PixelCMYK8 `liftM` M.unsafeRead vec idx
                   `ap` M.unsafeRead vec (idx + 1)
                   `ap` M.unsafeRead vec (idx + 2)
                   `ap` M.unsafeRead vec (idx + 3)
    unsafeWritePixel v idx (PixelCMYK8 r g b a) =
        M.unsafeWrite v idx r >> M.unsafeWrite v (idx + 1) g
                              >> M.unsafeWrite v (idx + 2) b
                              >> M.unsafeWrite v (idx + 3) a
instance ColorSpaceConvertible PixelCMYK8 PixelRGB8 where
  convertPixel (PixelCMYK8 c m y k) =
      PixelRGB8 (clampWord8 r) (clampWord8 g) (clampWord8 b)
    where
          clampWord8 = fromIntegral . (`unsafeShiftR` 8)
          ik :: Int
          ik = 255  fromIntegral k
          r = (255  fromIntegral c) * ik
          g = (255  fromIntegral m) * ik
          b = (255  fromIntegral y) * ik
integralRGBToCMYK :: (Bounded a, Integral a)
                  => (a -> a -> a -> a -> b)    
                  -> (a, a, a)                  
                  -> b                          
integralRGBToCMYK build (r, g, b) =
  build (clamp c) (clamp m) (clamp y) (fromIntegral kInt)
    where maxi = maxBound
          ir = fromIntegral $ maxi  r :: Int
          ig = fromIntegral $ maxi  g
          ib = fromIntegral $ maxi  b
          kInt = minimum [ir, ig, ib]
          ik = fromIntegral maxi  kInt
          c = (ir  kInt) `div` ik
          m = (ig  kInt) `div` ik
          y = (ib  kInt) `div` ik
          clamp = fromIntegral . (max 0)
instance ColorSpaceConvertible PixelRGB8 PixelCMYK8 where
  convertPixel (PixelRGB8 r g b) = integralRGBToCMYK PixelCMYK8 (r, g, b)
instance ColorPlane PixelCMYK8 PlaneCyan where
    toComponentIndex _ _ = 0
instance ColorPlane PixelCMYK8 PlaneMagenta where
    toComponentIndex _ _ = 1
instance ColorPlane PixelCMYK8 PlaneYellow where
    toComponentIndex _ _ = 2
instance ColorPlane PixelCMYK8 PlaneBlack where
    toComponentIndex _ _ = 3
instance Pixel PixelCMYK16 where
    type PixelBaseComponent PixelCMYK16 = Word16
    
    mixWith f (PixelCMYK16 ca ma ya ka) (PixelCMYK16 cb mb yb kb) =
        PixelCMYK16 (f 0 ca cb) (f 1 ma mb) (f 2 ya yb) (f 3 ka kb)
    
    colorMap f (PixelCMYK16 c m y k) = PixelCMYK16 (f c) (f m) (f y) (f k)
    componentCount _ = 4
    pixelAt image@(Image { imageData = arr }) x y = PixelCMYK16 (arr ! (baseIdx + 0))
                                                               (arr ! (baseIdx + 1))
                                                               (arr ! (baseIdx + 2))
                                                               (arr ! (baseIdx + 3))
        where baseIdx = pixelBaseIndex image x y
    readPixel image@(MutableImage { mutableImageData = arr }) x y = do
        rv <- arr .!!!. baseIdx
        gv <- arr .!!!. (baseIdx + 1)
        bv <- arr .!!!. (baseIdx + 2)
        av <- arr .!!!. (baseIdx + 3)
        return $ PixelCMYK16 rv gv bv av
        where baseIdx = mutablePixelBaseIndex image x y
    writePixel image@(MutableImage { mutableImageData = arr }) x y (PixelCMYK16 rv gv bv av) = do
        let baseIdx = mutablePixelBaseIndex image x y
        (arr .<-. (baseIdx + 0)) rv
        (arr .<-. (baseIdx + 1)) gv
        (arr .<-. (baseIdx + 2)) bv
        (arr .<-. (baseIdx + 3)) av
    unsafePixelAt v idx =
        PixelCMYK16 (V.unsafeIndex v idx)
                   (V.unsafeIndex v $ idx + 1)
                   (V.unsafeIndex v $ idx + 2)
                   (V.unsafeIndex v $ idx + 3)
    unsafeReadPixel vec idx =
        PixelCMYK16 `liftM` M.unsafeRead vec idx
                   `ap` M.unsafeRead vec (idx + 1)
                   `ap` M.unsafeRead vec (idx + 2)
                   `ap` M.unsafeRead vec (idx + 3)
    unsafeWritePixel v idx (PixelCMYK16 r g b a) =
        M.unsafeWrite v idx r >> M.unsafeWrite v (idx + 1) g
                              >> M.unsafeWrite v (idx + 2) b
                              >> M.unsafeWrite v (idx + 3) a
instance ColorSpaceConvertible PixelCMYK16 PixelRGB16 where
  convertPixel (PixelCMYK16 c m y k) =
      PixelRGB16 (clampWord16 r) (clampWord16 g) (clampWord16 b)
    where
          clampWord16 = fromIntegral . (`unsafeShiftR` 16)
          ik :: Int
          ik = 65535  fromIntegral k
          r = (65535  fromIntegral c) * ik
          g = (65535  fromIntegral m) * ik
          b = (65535  fromIntegral y) * ik
instance ColorPlane PixelCMYK16 PlaneCyan where
    toComponentIndex _ _ = 0
instance ColorPlane PixelCMYK16 PlaneMagenta where
    toComponentIndex _ _ = 1
instance ColorPlane PixelCMYK16 PlaneYellow where
    toComponentIndex _ _ = 2
instance ColorPlane PixelCMYK16 PlaneBlack where
    toComponentIndex _ _ = 3
gammaCorrection :: PixelF          
                -> Image PixelRGBF 
                -> Image PixelRGBF
gammaCorrection gammaVal = pixelMap gammaCorrector
  where gammaExponent = 1.0 / gammaVal
        fixVal v = v ** gammaExponent
        gammaCorrector (PixelRGBF r g b) =
            PixelRGBF (fixVal r) (fixVal g) (fixVal b)
toneMapping :: PixelF          
            -> Image PixelRGBF 
            -> Image PixelRGBF
toneMapping exposure img = Image (imageWidth img) (imageHeight img) scaledData
 where coeff = exposure * (exposure / maxBrightness + 1.0) / (exposure + 1.0);
       maxBrightness = pixelFold (\luma _ _ px -> max luma $ computeLuma px) 0 img
       scaledData = V.map (* coeff) $ imageData img