{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ViewPatterns #-}
#if __GLASGOW_HASKELL__ >= 800
{-# OPTIONS_GHC -Wno-redundant-constraints #-}
{-# LANGUAGE UndecidableSuperClasses #-}
#endif
module Graphics.Image.Interface (
Pixel, ColorSpace(..), AlphaSpace(..), Elevator(..),
BaseArray(..), Array(..),
MArray(..), createImage,
exchange,
index, defaultIndex, borderIndex, maybeIndex, Border(..), handleBorderIndex,
fromIx, toIx, checkDims
#if !MIN_VERSION_base(4,8,0)
, module Control.Applicative
, Foldable
#endif
) where
import Prelude hiding (and, map, product,
sum, zipWith)
#if !MIN_VERSION_base(4,8,0)
import Control.Applicative
#endif
import Control.DeepSeq (NFData (rnf), deepseq)
import Control.Monad.Primitive (PrimMonad (..))
import Control.Monad.ST
import Data.Foldable
import Data.Maybe (fromMaybe)
import Data.Proxy (Proxy (..))
import Data.Typeable (Typeable, showsTypeRep,
typeRep)
import qualified Data.Vector.Generic as VG
import qualified Data.Vector.Unboxed as VU
import GHC.Exts (Constraint)
import Graphics.Image.Interface.Elevator
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), Elevator e)
=> ColorSpace cs e where
type Components cs e
toComponents :: Pixel cs e -> Components cs e
fromComponents :: Components cs e -> Pixel cs e
promote :: e -> Pixel cs e
getPxC :: Pixel cs e -> cs -> e
setPxC :: Pixel cs e -> cs -> e -> Pixel cs e
mapPxC :: (cs -> e -> e) -> Pixel cs e -> Pixel cs e
liftPx :: (e -> e) -> Pixel cs e -> Pixel cs e
liftPx2 :: (e -> e -> e) -> Pixel cs e -> Pixel cs e -> Pixel cs e
foldlPx2 :: (b -> e -> e -> b) -> b -> Pixel cs e -> Pixel cs e -> b
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
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
class (ColorSpace (Opaque cs) e, ColorSpace cs e) => AlphaSpace cs e where
type Opaque cs
getAlpha :: Pixel cs e -> e
addAlpha :: e -> Pixel (Opaque cs) e -> Pixel cs e
dropAlpha :: Pixel cs e -> Pixel (Opaque cs) e
class (Typeable arr, ColorSpace cs e, SuperClass arr cs e) =>
BaseArray arr cs e where
type SuperClass arr cs e :: Constraint
data Image arr cs e
dims :: Image arr cs e -> (Int, Int)
class (VG.Vector (Vector arr) (Pixel cs e),
MArray (Manifest arr) cs e, BaseArray arr cs e) => Array arr cs e where
type Manifest arr :: *
type Vector arr :: * -> *
makeImage :: (Int, Int)
-> ((Int, Int) -> Pixel cs e)
-> Image arr cs e
makeImageWindowed :: (Int, Int)
-> (Int, Int)
-> (Int, Int)
-> ((Int, Int) -> Pixel cs e)
-> ((Int, Int) -> Pixel cs e)
-> Image arr cs e
scalar :: Pixel cs e -> Image arr cs e
index00 :: Image arr cs e -> Pixel cs e
map :: Array arr cs' e' =>
(Pixel cs' e' -> Pixel cs e)
-> Image arr cs' e'
-> Image arr cs e
imap :: Array arr cs' e' =>
((Int, Int) -> Pixel cs' e' -> Pixel cs e)
-> Image arr cs' e'
-> Image arr cs e
zipWith :: (Array arr cs1 e1, Array arr cs2 e2) =>
(Pixel cs1 e1 -> Pixel cs2 e2 -> Pixel cs e)
-> Image arr cs1 e1 -> Image arr cs2 e2 -> Image arr cs e
izipWith :: (Array arr cs1 e1, Array arr cs2 e2) =>
((Int, Int) -> Pixel cs1 e1 -> Pixel cs2 e2 -> Pixel cs e)
-> Image arr cs1 e1 -> Image arr cs2 e2 -> Image arr cs e
traverse :: Array arr cs' e' =>
Image arr cs' e'
-> ((Int, Int) -> (Int, Int))
-> (((Int, Int) -> Pixel cs' e') ->
(Int, Int) -> Pixel cs e)
-> Image arr cs e
traverse2 :: (Array arr cs1 e1, Array arr cs2 e2) =>
Image arr cs1 e1
-> Image arr cs2 e2
-> ((Int, Int) -> (Int, Int) -> (Int, Int))
-> (((Int, Int) -> Pixel cs1 e1) ->
((Int, Int) -> Pixel cs2 e2) ->
(Int, Int) -> Pixel cs e)
-> Image arr cs e
transpose :: Image arr cs e -> Image arr cs e
backpermute :: (Int, Int)
-> ((Int, Int) -> (Int, Int))
-> Image arr cs e
-> Image arr cs e
fromLists :: [[Pixel cs e]]
-> Image arr cs e
(|*|) :: Image arr cs e -> Image arr cs e -> Image arr cs e
fold :: (Pixel cs e -> Pixel cs e -> Pixel cs e)
-> Pixel cs e
-> Image arr cs e
-> Pixel cs e
foldIx :: (Pixel cs e -> (Int, Int) -> Pixel cs e -> Pixel cs e)
-> Pixel cs e
-> Image arr cs e
-> Pixel cs e
eq :: Image arr cs e -> Image arr cs e -> Bool
compute :: Image arr cs e -> Image arr cs e
toManifest :: Image arr cs e -> Image (Manifest arr) cs e
toVector :: Image arr cs e -> Vector arr (Pixel cs e)
fromVector :: (Int, Int) -> Vector arr (Pixel cs e) -> Image arr cs e
class BaseArray arr cs e => MArray arr cs e where
data MImage s arr cs e
unsafeIndex :: Image arr cs e -> (Int, Int) -> Pixel cs e
deepSeqImage :: Image arr cs e -> a -> a
foldl :: (a -> Pixel cs e -> a) -> a -> Image arr cs e -> a
foldr :: (Pixel cs e -> a -> a) -> a -> Image arr cs e -> a
makeImageM :: (Functor m, Monad m) =>
(Int, Int)
-> ((Int, Int) -> m (Pixel cs e))
-> m (Image arr cs e)
mapM :: (MArray arr cs' e', Functor m, Monad m) =>
(Pixel cs' e' -> m (Pixel cs e)) -> Image arr cs' e' -> m (Image arr cs e)
mapM_ :: (Functor m, Monad m) => (Pixel cs e -> m b) -> Image arr cs e -> m ()
foldM :: (Functor m, Monad m) => (a -> Pixel cs e -> m a) -> a -> Image arr cs e -> m a
foldM_ :: (Functor m, Monad m) => (a -> Pixel cs e -> m a) -> a -> Image arr cs e -> m ()
mdims :: MImage s arr cs e -> (Int, Int)
thaw :: (Functor m, PrimMonad m) =>
Image arr cs e -> m (MImage (PrimState m) arr cs e)
freeze :: (Functor m, PrimMonad m) =>
MImage (PrimState m) arr cs e -> m (Image arr cs e)
new :: (Functor m, PrimMonad m) =>
(Int, Int) -> m (MImage (PrimState m) arr cs e)
read :: (Functor m, PrimMonad m) =>
MImage (PrimState m) arr cs e -> (Int, Int) -> m (Pixel cs e)
write :: (Functor m, PrimMonad m) =>
MImage (PrimState m) arr cs e -> (Int, Int) -> Pixel cs e -> m ()
swap :: (Functor m, PrimMonad m) =>
MImage (PrimState m) arr cs e -> (Int, Int) -> (Int, Int) -> m ()
createImage
:: MArray arr cs e
=> (forall s. ST s (MImage s arr cs e)) -> Image arr cs e
createImage create = runST (create >>= freeze)
exchange :: (Array arr' cs e, Array arr cs e) =>
arr
-> Image arr' cs e
-> Image arr cs e
exchange _ img@(dims -> (1, 1)) = scalar $ index00 img
exchange _ img = fromVector (dims img) $ VG.convert $ toVector img
{-# INLINE exchange #-}
data Border px =
Fill !px
| Wrap
| Edge
| Reflect
| Continue
deriving Show
handleBorderIndex :: Border px
-> (Int, Int)
-> ((Int, Int) -> px)
-> (Int, Int)
-> px
handleBorderIndex ~border !(m, n) getPx !(i, j) =
if north || east || south || west
then case border of
Fill px -> px
Wrap -> getPx (i `mod` m, j `mod` n)
Edge -> getPx (if north then 0 else if south then m - 1 else i,
if west then 0 else if east then n - 1 else j)
Reflect -> getPx (if north then (abs i - 1) `mod` m else
if south then (-i - 1) `mod` m else i,
if west then (abs j - 1) `mod` n else
if east then (-j - 1) `mod` n else j)
Continue -> getPx (if north then abs i `mod` m else
if south then (-i - 2) `mod` m else i,
if west then abs j `mod` n else
if east then (-j - 2) `mod` n else j)
else getPx (i, j)
where
!north = i < 0
!south = i >= m
!west = j < 0
!east = j >= n
{-# INLINE handleBorderIndex #-}
index :: MArray arr cs e => Image arr cs e -> (Int, Int) -> Pixel cs e
index !img !ix = borderIndex (error $ show img ++ " - Index out of bounds: " ++ show ix) img ix
{-# INLINE index #-}
defaultIndex :: MArray arr cs e =>
Pixel cs e -> Image arr cs e -> (Int, Int) -> Pixel cs e
defaultIndex !px !img = handleBorderIndex (Fill px) (dims img) (index img)
{-# INLINE defaultIndex #-}
borderIndex :: MArray arr cs e =>
Border (Pixel cs e) -> Image arr cs e -> (Int, Int) -> Pixel cs e
borderIndex ~atBorder !img = handleBorderIndex atBorder (dims img) (unsafeIndex img)
{-# INLINE borderIndex #-}
maybeIndex :: MArray arr cs e =>
Image arr cs e -> (Int, Int) -> Maybe (Pixel cs e)
maybeIndex !img@(dims -> (m, n)) !(i, j) =
if i >= 0 && j >= 0 && i < m && j < n then Just $ unsafeIndex img (i, j) else Nothing
{-# INLINE maybeIndex #-}
fromIx :: Int
-> (Int, Int)
-> Int
fromIx !n !(i, j) = n * i + j
{-# INLINE fromIx #-}
toIx :: Int
-> Int
-> (Int, Int)
toIx !n !k = divMod k n
{-# INLINE toIx #-}
checkDims :: String -> (Int, Int) -> (Int, Int)
checkDims err !sz@(m, n)
| m <= 0 || n <= 0 =
error $
show err ++ ": dimensions are expected to be positive: " ++ show sz
| otherwise = sz
{-# INLINE checkDims #-}
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 (Foldable (Pixel cs), NFData e) => NFData (Pixel cs e) where
rnf = foldr' deepseq ()
{-# INLINE rnf #-}
instance Array arr cs e => Eq (Image arr cs e) where
(==) = eq
{-# INLINE (==) #-}
instance Array arr cs e => Num (Image arr cs e) where
(+) = zipWith (+)
{-# INLINE (+) #-}
(-) = zipWith (-)
{-# INLINE (-) #-}
(*) = zipWith (*)
{-# INLINE (*) #-}
abs = map abs
{-# INLINE abs #-}
signum = map signum
{-# INLINE signum #-}
fromInteger = scalar . fromInteger
{-# INLINE fromInteger #-}
instance (Fractional (Pixel cs e), Array arr cs e) =>
Fractional (Image arr cs e) where
(/) = zipWith (/)
{-# INLINE (/) #-}
fromRational = scalar . fromRational
{-# INLINE fromRational #-}
instance (Floating (Pixel cs e), Array arr cs e) =>
Floating (Image arr cs e) where
pi = scalar pi
{-# INLINE pi #-}
exp = map exp
{-# INLINE exp #-}
log = map log
{-# INLINE log #-}
sin = map sin
{-# INLINE sin #-}
cos = map cos
{-# INLINE cos #-}
asin = map asin
{-# INLINE asin #-}
atan = map atan
{-# INLINE atan #-}
acos = map acos
{-# INLINE acos #-}
sinh = map sinh
{-# INLINE sinh #-}
cosh = map cosh
{-# INLINE cosh #-}
asinh = map asinh
{-# INLINE asinh #-}
atanh = map atanh
{-# INLINE atanh #-}
acosh = map acosh
{-# INLINE acosh #-}
instance MArray arr cs e => NFData (Image arr cs e) where
rnf img = img `deepSeqImage` ()
{-# INLINE rnf #-}
instance BaseArray arr cs e =>
Show (Image arr cs e) where
show (dims -> (m, n)) =
"<Image " ++
showsTypeRep (typeRep (Proxy :: Proxy arr)) " " ++
showsTypeRep (typeRep (Proxy :: Proxy cs)) " (" ++
showsTypeRep (typeRep (Proxy :: Proxy e)) "): " ++
show m ++ "x" ++ show n ++ ">"
instance MArray arr cs e =>
Show (MImage st arr cs e) where
show (mdims -> (m, n)) =
"<MutableImage " ++
showsTypeRep (typeRep (Proxy :: Proxy arr)) " " ++
showsTypeRep (typeRep (Proxy :: Proxy cs)) " (" ++
showsTypeRep (typeRep (Proxy :: Proxy e)) "): " ++
show m ++ "x" ++ show n ++ ">"