module Graphics.Image.Interface.Vector.Generic (
G(..), Image(..), fromVector
) where
import Prelude hiding (map, zipWith)
import qualified Prelude as P (map)
import Control.DeepSeq (NFData, deepseq)
import Control.Monad
import Control.Monad.ST
#if !MIN_VERSION_base(4,8,0)
import Data.Functor
#endif
import Data.Primitive.MutVar
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Generic as VG
import qualified Data.Vector.Generic.Mutable as MVG
import Graphics.Image.Interface as I
data G r = G r
instance Show r => Show (G r) where
show (G r) = "Vector " ++ show r
instance SuperClass (G r) cs e => BaseArray (G r) cs e where
type SuperClass (G r) cs e =
(Show r, ColorSpace cs e,
VG.Vector (Vector r) Int, VG.Vector (Vector r) Bool,
VG.Vector (Vector r) (Pixel cs e), NFData ((Vector r) (Pixel cs e)))
data Image (G r) cs e = VScalar !(Pixel cs e)
| VImage !Int
!Int
!((Vector (G r)) (Pixel cs e))
dims (VImage m n _) = (m, n)
dims (VScalar _) = (1, 1)
instance (VG.Vector (Vector r) (Pixel cs e),
MArray (G r) cs e, BaseArray (G r) cs e) => Array (G r) cs e where
type Manifest (G r) = G r
type Vector (G r) = Vector r
makeImage !(checkDims "(G r).makeImage" -> (m, n)) f =
VImage m n $ VG.generate (m * n) (f . toIx n)
makeImageWindowed !sz !((it, jt), (ib, jb)) getWindowPx getBorderPx =
VImage m n $ VG.create generate where
!(m, n) = checkDims "(G r).makeImageWindowed" sz
nestedLoop :: (VG.Mutable (Vector r)) s (Pixel cs e)
-> ((Int, Int) -> Pixel cs e)
-> Int -> Int -> Int -> Int
-> ST s ()
nestedLoop !mv !getPx !fi !fj !ti !tj = do
VU.forM_ (VU.enumFromN fi (tifi)) $ \i ->
VU.forM_ (VU.enumFromN fj (tjfj)) $ \j ->
MVG.unsafeWrite mv (fromIx n (i, j)) (getPx (i, j))
generate :: ST s ((VG.Mutable (Vector (G r))) s (Pixel cs e))
generate = do
mv <- MVG.unsafeNew (m*n)
nestedLoop mv getBorderPx 0 0 ib n
nestedLoop mv getBorderPx it 0 ib jt
nestedLoop mv getWindowPx it jt ib jb
nestedLoop mv getBorderPx it jb ib n
nestedLoop mv getBorderPx ib 0 m n
return mv
scalar = VScalar
index00 (VScalar px) = px
index00 (VImage _ _ v) = v VG.! 0
map f (VScalar px) = VScalar (f px)
map f (VImage m n v) = VImage m n (VG.map f v)
imap f (VScalar px) = VScalar (f (0, 0) px)
imap f (VImage m n v) = VImage m n (VG.imap (\ !k !px -> f (toIx n k) px) v)
zipWith f (VScalar px1) (VScalar px2) = VScalar (f px1 px2)
zipWith f (VScalar px1) (VImage m n v2) = VImage m n (VG.map (f px1) v2)
zipWith f (VImage m n v1) (VScalar px2) = VImage m n (VG.map (`f` px2) v1)
zipWith f img1@(VImage m1 n1 v1) img2@(VImage m2 n2 v2) =
if m1 /= m2 || n1 /= n2
then error ("zipWith: Images must be of the same dimensions, received: "++
show img1++" and "++show img2++".")
else VImage m1 n1 (VG.zipWith f v1 v2)
izipWith f (VScalar px1) (VScalar px2) = VScalar (f (0, 0) px1 px2)
izipWith f (VScalar px1) (VImage m n v2) =
VImage m n (VG.imap (\ !k !px2 -> f (toIx n k) px1 px2) v2)
izipWith f (VImage m n v1) (VScalar px2) =
VImage m n (VG.imap (\ !k !px1 -> f (toIx n k) px1 px2) v1)
izipWith f img1@(VImage m1 n1 v1) img2@(VImage m2 n2 v2) =
if m1 /= m2 || n1 /= n2
then error ("izipWith: Images must be of the same dimensions, received: "++
show img1++" and "++show img2++".")
else VImage m1 n1 (VG.izipWith (\ !k !px1 !px2 -> f (toIx n1 k) px1 px2) v1 v2)
traverse !img getNewDims getNewPx = makeImage (getNewDims (dims img)) (getNewPx (index img))
traverse2 !img1 !img2 getNewDims getNewPx =
makeImage (getNewDims (dims img1) (dims img2)) (getNewPx (index img1) (index img2))
transpose !img = backpermute (n, m) movePx img where
!(m, n) = dims img
movePx !(i, j) = (j, i)
backpermute !(checkDims "(G r).backpermute" -> (m, n)) !f (VImage _ n' v) =
VImage m n $ VG.backpermute v $ VG.generate (m*n) (fromIx n' . f . toIx n)
backpermute !sz _ (VScalar px) = makeImage sz (const px)
fromLists !ls = if all (== n) (P.map length ls)
then VImage m n . VG.fromList . concat $ ls
else error "fromLists: Inner lists are of different lengths."
where
!(m, n) = checkDims "(G r).fromLists" (length ls, length $ head ls)
fold !f !px0 (VImage _ _ v) = VG.foldl' f px0 v
fold !f !px0 (VScalar px) = f px0 px
foldIx !f !px0 (VImage _ n v) = VG.ifoldl' f' px0 v where
f' !acc !k !px = f acc (toIx n k) px
foldIx !f !px0 (VScalar px) = f px0 (0,0) px
(|*|) img1@(VImage m1 n1 v1) !img2@VImage {} =
if n1 /= m2
then error ("Inner dimensions of multiplying images must be the same, but received: "++
show img1 ++" X "++ show img2)
else
makeImage (m1, n2) getPx where
VImage n2 m2 v2 = transpose img2
getPx !(i, j) = VG.sum $ VG.zipWith (*) (VG.slice (i*n1) n1 v1) (VG.slice (j*m2) m2 v2)
(|*|) (VScalar px1) (VScalar px2) = VScalar (px1 * px2)
(|*|) _ _ = error "Scalar Images cannot be multiplied."
eq (VImage m1 n1 v1) (VImage m2 n2 v2) =
m1 == m2 && n1 == n2 && VG.all id (VG.zipWith (==) v1 v2)
eq (VScalar px1) (VScalar px2) = px1 == px2
eq (VImage 1 1 v1) (VScalar px2) = v1 VG.! 0 == px2
eq (VScalar px1) (VImage 1 1 v2) = v2 VG.! 0 == px1
eq _ _ = False
compute (VImage m n v) = v `deepseq` (VImage m n v)
compute (VScalar px) = px `seq` (VScalar px)
toManifest = id
toVector (VImage _ _ v) = VG.convert v
toVector (VScalar px) = VG.singleton px
fromVector !(m, n) !v
| m * n /= VG.length v =
error $ "fromVector: m * n doesn't equal the length of a Vector: " ++
show m ++ " * " ++ show n ++ " /= " ++ show (VG.length v)
| m == 1 && n == 1 = VScalar (VG.unsafeIndex v 0)
| otherwise = VImage m n v
instance (BaseArray (G r) cs e) => MArray (G r) cs e where
data MImage s (G r) cs e = MVImage !Int !Int ((VG.Mutable (Vector (G r))) s (Pixel cs e))
| MVScalar (MutVar s (Pixel cs e))
unsafeIndex (VImage _ n v) !ix = VG.unsafeIndex v (fromIx n ix)
unsafeIndex (VScalar px) _ = px
deepSeqImage (VImage m n v) = m `seq` n `seq` deepseq v
deepSeqImage (VScalar px) = seq px
foldl f !a (VImage _ _ v) = VG.foldl' f a v
foldl f !a (VScalar px) = f a px
foldr f !a (VImage _ _ v) = VG.foldr' f a v
foldr f !a (VScalar px) = f px a
makeImageM !(checkDims "(G r).makeImageM" -> (m, n)) !f =
VImage m n <$> VG.generateM (m * n) (f . toIx n)
mapM f (VImage m n v) = VImage m n <$> VG.mapM f v
mapM f (VScalar px) = VScalar <$> f px
mapM_ f (VImage _ _ v) = VG.mapM_ f v
mapM_ f (VScalar px) = void $ f px
foldM f !a (VImage _ _ v) = VG.foldM' f a v
foldM f !a (VScalar px) = f a px
foldM_ f !a (VImage _ _ v) = VG.foldM'_ f a v
foldM_ f !a (VScalar px) = void $ f a px
mdims (MVImage m n _) = (m, n)
mdims (MVScalar _) = (1, 1)
thaw (VImage m n v) = MVImage m n <$> VG.thaw v
thaw (VScalar px) = MVScalar <$> newMutVar px
freeze (MVImage m n mv) = VImage m n <$> VG.freeze mv
freeze (MVScalar mpx) = VScalar <$> readMutVar mpx
new (m, n) = MVImage m n <$> MVG.new (m*n)
read (MVImage _ n mv) !ix = MVG.read mv (fromIx n ix)
read (MVScalar mpx) !ix = do
unless ((0, 0) == ix) $ error $ "Index out of bounds: " ++ show ix
readMutVar mpx
write (MVImage _ n mv) !ix !px = MVG.write mv (fromIx n ix) px
write (MVScalar mv) !ix !px = do
unless ((0, 0) == ix) $ error $ "Index out of bounds: " ++ show ix
writeMutVar mv px
swap (MVImage _ n mv) !ix1 !ix2 = MVG.swap mv (fromIx n ix1) (fromIx n ix2)
swap _ _ _ = return ()