{-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-} -- | -- Module : Graphics.ColorSpace.Internal -- Copyright : (c) Alexey Kuleshevich 2018 -- License : BSD3 -- Maintainer : Alexey Kuleshevich -- Stability : experimental -- Portability : non-portable -- module Graphics.ColorSpace.Internal ( Pixel , ColorSpace(..) , AlphaSpace(..) , module Graphics.ColorSpace.Elevator ) where import Control.DeepSeq (NFData (rnf), deepseq) import Control.Monad (liftM) import Data.Default import Data.Foldable import Data.Maybe (fromMaybe) import Data.Typeable import qualified Data.Vector.Generic as V import qualified Data.Vector.Generic.Mutable as VM import qualified Data.Vector.Storable as VS import qualified Data.Vector.Unboxed as VU import Graphics.ColorSpace.Elevator -- | A Pixel family with a color space and a precision of elements. data family Pixel cs e :: * class (Eq cs, Enum cs, Show cs, Bounded cs, Typeable cs, Eq (Pixel cs e), VU.Unbox (Components cs e), VS.Storable (Pixel cs e), Elevator e) => ColorSpace cs e where type Components cs e -- | Convert a Pixel to a representation suitable for storage as an unboxed -- element, usually a tuple of channels. toComponents :: Pixel cs e -> Components cs e -- | Convert from an elemnt representation back to a Pixel. fromComponents :: Components cs e -> Pixel cs e -- | Construt a Pixel by replicating the same value across all of the components. promote :: e -> Pixel cs e -- | Retrieve Pixel's component value getPxC :: Pixel cs e -> cs -> e -- | Set Pixel's component value setPxC :: Pixel cs e -> cs -> e -> Pixel cs e -- | Map a channel aware function over all Pixel's components. mapPxC :: (cs -> e -> e) -> Pixel cs e -> Pixel cs e -- | Map a function over all Pixel's componenets. liftPx :: (e -> e) -> Pixel cs e -> Pixel cs e -- | Zip two Pixels with a function. liftPx2 :: (e -> e -> e) -> Pixel cs e -> Pixel cs e -> Pixel cs e -- | Left fold on two pixels a the same time. foldlPx2 :: (b -> e -> e -> b) -> b -> Pixel cs e -> Pixel cs e -> b -- | Right fold over all Pixel's components. foldrPx :: (e -> b -> b) -> b -> Pixel cs e -> b foldrPx f !z0 !xs = foldlPx f' id xs z0 where f' k x !z = k $! f x z -- | Left strict fold over all Pixel's components. foldlPx :: (b -> e -> b) -> b -> Pixel cs e -> b foldlPx f !z0 !xs = foldrPx f' id xs z0 where f' x k !z = k $! f z x foldl1Px :: (e -> e -> e) -> Pixel cs e -> e foldl1Px f !xs = fromMaybe (error "foldl1Px: empty Pixel") (foldlPx mf Nothing xs) where mf m !y = Just (case m of Nothing -> y Just x -> f x y) toListPx :: Pixel cs e -> [e] toListPx !px = foldr' f [] (enumFrom (toEnum 0)) where f !cs !ls = getPxC px cs:ls -- | A color space that supports transparency. class (ColorSpace (Opaque cs) e, ColorSpace cs e) => AlphaSpace cs e where -- | A corresponding opaque version of this color space. type Opaque cs -- | Get an alpha channel of a transparant pixel. getAlpha :: Pixel cs e -> e -- | Add an alpha channel to an opaque pixel. -- -- @ addAlpha 0 (PixelHSI 1 2 3) == PixelHSIA 1 2 3 0 @ addAlpha :: e -> Pixel (Opaque cs) e -> Pixel cs e -- | Convert a transparent pixel to an opaque one by dropping the alpha -- channel. -- -- @ dropAlpha (PixelRGBA 1 2 3 4) == PixelRGB 1 2 3 @ -- dropAlpha :: Pixel cs e -> Pixel (Opaque cs) e instance ColorSpace cs e => Default (Pixel cs e) where def = promote 0 {-# INLINE def #-} instance ColorSpace cs e => Num (Pixel cs e) where (+) = liftPx2 (+) {-# INLINE (+) #-} (-) = liftPx2 (-) {-# INLINE (-) #-} (*) = liftPx2 (*) {-# INLINE (*) #-} abs = liftPx abs {-# INLINE abs #-} signum = liftPx signum {-# INLINE signum #-} fromInteger = promote . fromInteger {-# INLINE fromInteger #-} instance (ColorSpace cs e, Fractional e) => Fractional (Pixel cs e) where (/) = liftPx2 (/) {-# INLINE (/) #-} recip = liftPx recip {-# INLINE recip #-} fromRational = promote . fromRational {-# INLINE fromRational #-} instance (ColorSpace cs e, Floating e) => Floating (Pixel cs e) where pi = promote pi {-# INLINE pi #-} exp = liftPx exp {-# INLINE exp #-} log = liftPx log {-# INLINE log #-} sin = liftPx sin {-# INLINE sin #-} cos = liftPx cos {-# INLINE cos #-} asin = liftPx asin {-# INLINE asin #-} atan = liftPx atan {-# INLINE atan #-} acos = liftPx acos {-# INLINE acos #-} sinh = liftPx sinh {-# INLINE sinh #-} cosh = liftPx cosh {-# INLINE cosh #-} asinh = liftPx asinh {-# INLINE asinh #-} atanh = liftPx atanh {-# INLINE atanh #-} acosh = liftPx acosh {-# INLINE acosh #-} instance (ColorSpace cs e, Bounded e) => Bounded (Pixel cs e) where maxBound = promote maxBound {-# INLINE maxBound #-} minBound = promote minBound {-# INLINE minBound #-} instance (ColorSpace cs e, NFData e) => NFData (Pixel cs e) where rnf = foldrPx deepseq () {-# INLINE rnf #-} -- | Unboxing of a `Pixel`. instance ColorSpace cs e => VU.Unbox (Pixel cs e) newtype instance VU.MVector s (Pixel cs e) = MV_Pixel (VU.MVector s (Components cs e)) instance ColorSpace cs e => VM.MVector VU.MVector (Pixel cs e) where basicLength (MV_Pixel mvec) = VM.basicLength mvec {-# INLINE basicLength #-} basicUnsafeSlice idx len (MV_Pixel mvec) = MV_Pixel (VM.basicUnsafeSlice idx len mvec) {-# INLINE basicUnsafeSlice #-} basicOverlaps (MV_Pixel mvec) (MV_Pixel mvec') = VM.basicOverlaps mvec mvec' {-# INLINE basicOverlaps #-} basicUnsafeNew len = MV_Pixel `liftM` VM.basicUnsafeNew len {-# INLINE basicUnsafeNew #-} basicUnsafeReplicate len val = MV_Pixel `liftM` VM.basicUnsafeReplicate len (toComponents val) {-# INLINE basicUnsafeReplicate #-} basicUnsafeRead (MV_Pixel mvec) idx = fromComponents `liftM` VM.basicUnsafeRead mvec idx {-# INLINE basicUnsafeRead #-} basicUnsafeWrite (MV_Pixel mvec) idx val = VM.basicUnsafeWrite mvec idx (toComponents val) {-# INLINE basicUnsafeWrite #-} basicClear (MV_Pixel mvec) = VM.basicClear mvec {-# INLINE basicClear #-} basicSet (MV_Pixel mvec) val = VM.basicSet mvec (toComponents val) {-# INLINE basicSet #-} basicUnsafeCopy (MV_Pixel mvec) (MV_Pixel mvec') = VM.basicUnsafeCopy mvec mvec' {-# INLINE basicUnsafeCopy #-} basicUnsafeMove (MV_Pixel mvec) (MV_Pixel mvec') = VM.basicUnsafeMove mvec mvec' {-# INLINE basicUnsafeMove #-} basicUnsafeGrow (MV_Pixel mvec) len = MV_Pixel `liftM` VM.basicUnsafeGrow mvec len {-# INLINE basicUnsafeGrow #-} #if MIN_VERSION_vector(0,11,0) basicInitialize (MV_Pixel mvec) = VM.basicInitialize mvec {-# INLINE basicInitialize #-} #endif newtype instance VU.Vector (Pixel cs e) = V_Pixel (VU.Vector (Components cs e)) instance (ColorSpace cs e) => V.Vector VU.Vector (Pixel cs e) where basicUnsafeFreeze (MV_Pixel mvec) = V_Pixel `liftM` V.basicUnsafeFreeze mvec {-# INLINE basicUnsafeFreeze #-} basicUnsafeThaw (V_Pixel vec) = MV_Pixel `liftM` V.basicUnsafeThaw vec {-# INLINE basicUnsafeThaw #-} basicLength (V_Pixel vec) = V.basicLength vec {-# INLINE basicLength #-} basicUnsafeSlice idx len (V_Pixel vec) = V_Pixel (V.basicUnsafeSlice idx len vec) {-# INLINE basicUnsafeSlice #-} basicUnsafeIndexM (V_Pixel vec) idx = fromComponents `liftM` V.basicUnsafeIndexM vec idx {-# INLINE basicUnsafeIndexM #-} basicUnsafeCopy (MV_Pixel mvec) (V_Pixel vec) = V.basicUnsafeCopy mvec vec {-# INLINE basicUnsafeCopy #-} elemseq (V_Pixel vec) val = V.elemseq vec (toComponents val) {-# INLINE elemseq #-}