{-# OPTIONS -fno-warn-orphans #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ViewPatterns #-} -- | -- Module : Graphics.Image.Interface.Repa.Internal -- Copyright : (c) Alexey Kuleshevich 2016 -- License : BSD3 -- Maintainer : Alexey Kuleshevich -- Stability : experimental -- Portability : non-portable -- module Graphics.Image.Interface.Repa.Internal ( RD(..), RP(..), RS(..), computeP, computeS, delay, fromRepaArray, toRepaArray ) where #if MIN_VERSION_base(4,8,0) import Prelude hiding (map, zipWith, foldl, foldr, mapM, mapM_, read, traverse) #else import Prelude hiding (map, zipWith, foldl, foldr, mapM, mapM_, read) #endif import qualified Prelude as P (map, mapM_) import Graphics.Image.Interface import Graphics.Image.ColorSpace.Binary (Bit(..)) import Graphics.Image.Interface.Vector.Unboxed (VU(..), fromUnboxedVector, toUnboxedVector) import Control.Monad (liftM) import Data.Array.Repa.Repr.Unboxed (Unbox) import qualified Data.Vector.Unboxed as V ((!)) import Data.Function (on) import Data.Typeable (Typeable) import Data.Array.Repa.Index import qualified Data.Array.Repa as R import qualified Data.Array.Repa.Eval as R (Elt(..), suspendedComputeP) -- | Repa 'D'elayed Array representation, which allows for fusion of computation. data RD = RD -- | Repa 'U'nboxed Array representation, which is computed in parallel. data RP = RP -- | Repa 'U'nboxed Array representation, which is computed sequentially. data RS = RS instance Show RD where show _ = "RepaDelayed" instance Show RP where show _ = "RepaParallel" instance Show RS where show _ = "RepaSequential" instance Elt RD cs e => Array RD cs e where type Elt RD cs e = (ColorSpace cs, Num e, Typeable e, R.Elt e, Unbox e, R.Elt (PixelElt cs e), Unbox (PixelElt cs e), R.Elt (Pixel cs e), Unbox (Pixel cs e)) data Image RD cs e = RScalar !(Pixel cs e) | RUImage !(R.Array R.U R.DIM2 (Pixel cs e)) | RDImage !(R.Array R.D R.DIM2 (Pixel cs e)) dims (RScalar _ ) = (1, 1) dims (RUImage (R.extent -> (Z :. m :. n))) = (m, n) dims (RDImage (R.extent -> (Z :. m :. n))) = (m, n) {-# INLINE dims #-} singleton = RScalar {-# INLINE singleton #-} makeImage !(m, n) !f = RDImage $ R.fromFunction (Z :. m :. n) (f . shT2) {-# INLINE makeImage #-} map f (RScalar px) = RScalar (f px) map f (getDelayed -> arr) = RDImage (R.map f arr) {-# INLINE map #-} imap f (RScalar px) = RScalar (f (0, 0) px) imap f (getDelayed -> arr) = RDImage (R.zipWith f (R.fromFunction (R.extent arr) shT2) arr) {-# INLINE imap #-} zipWith f (RScalar px1) (RScalar px2) = RScalar (f px1 px2) zipWith f (RScalar px1) (getDelayed -> arr2) = RDImage (R.map (f px1) arr2) zipWith f (getDelayed -> arr1) (RScalar px2) = RDImage (R.map (`f` px2) arr1) zipWith f (getDelayed -> arr1) (getDelayed -> arr2) = RDImage (R.zipWith f arr1 arr2) {-# INLINE zipWith #-} izipWith f (RScalar px1) (RScalar px2) = RScalar (f (0, 0) px1 px2) izipWith f (RScalar px1) !img2 = imap (flip f px1) img2 izipWith f !img1 (RScalar px2) = imap (\ !ix !px -> f ix px px2) img1 izipWith f (getDelayed -> arr1) (getDelayed -> arr2) = RDImage (R.traverse2 arr1 arr2 const getNewPx) where getNewPx !getPx1 !getPx2 !sh = f (shT2 sh) (getPx1 sh) (getPx2 sh) {-# INLINE getNewPx #-} {-# INLINE izipWith #-} traverse (getDelayed -> arr) newDims newPx = RDImage $ R.traverse arr (tSh2 . newDims . shT2) newPixel where newPixel getPx = newPx (getPx . tSh2) . shT2 {-# INLINE traverse #-} traverse2 (getDelayed -> arr1) (getDelayed -> arr2) newDims newPx = RDImage $ R.traverse2 arr1 arr2 (((.).(.)) tSh2 (newDims `on` shT2)) newPixel where newPixel getPx1 getPx2 = newPx (getPx1 . tSh2) (getPx2 . tSh2) . shT2 {-# INLINE traverse2 #-} transpose (RDImage arr) = RDImage (R.transpose arr) transpose (RUImage arr) = RDImage (R.transpose arr) transpose !img = img {-# INLINE transpose #-} backpermute _ _ img@(RScalar _) = img backpermute (tSh2 -> sh) g (getDelayed -> arr) = RDImage (R.backpermute sh (tSh2 . g . shT2) arr) {-# INLINE backpermute #-} fromLists !ls = if isSquare then RUImage . R.fromListUnboxed (Z :. m :. n) . concat $ ls else error "fromLists: Inner lists do not all have an equal length." where !(m, n) = (length ls, length $ head ls) !isSquare = (n > 0) && all (==n) (P.map length ls) {-# INLINE fromLists #-} instance Elt RS cs e => Array RS cs e where type Elt RS cs e = (ColorSpace cs, R.Elt e, Unbox e, Num e, Typeable e, R.Elt (PixelElt cs e), Unbox (PixelElt cs e), R.Elt (Pixel cs e), Unbox (Pixel cs e)) data Image RS cs e = RSImage !(Image RD cs e) dims (RSImage img) = dims img {-# INLINE dims #-} makeImage !ix !f = computeS (makeImage ix f :: Image RD cs e) {-# INLINE makeImage #-} singleton = RSImage . singleton {-# INLINE singleton #-} map !f (RSImage img) = computeS . map f $ img {-# INLINE map #-} imap !f (RSImage img) = computeS . imap f $ img {-# INLINE imap #-} zipWith !f (RSImage img1) (RSImage img2) = computeS . zipWith f img1 $ img2 {-# INLINE zipWith #-} izipWith !f (RSImage img1) (RSImage img2) = computeS . izipWith f img1 $ img2 {-# INLINE izipWith #-} traverse (RSImage img) newDims = computeS . traverse img newDims {-# INLINE traverse #-} traverse2 (RSImage img1) (RSImage img2) newDims = computeS . traverse2 img1 img2 newDims {-# INLINE traverse2 #-} transpose (RSImage img) = computeS . transpose $ img {-# INLINE transpose #-} backpermute !f !g (RSImage img) = computeS $ backpermute f g img {-# INLINE backpermute #-} fromLists = RSImage . fromLists {-# INLINE fromLists #-} instance Elt RP cs e => Array RP cs e where type Elt RP cs e = (ColorSpace cs, R.Elt e, Unbox e, Num e, Typeable e, R.Elt (PixelElt cs e), Unbox (PixelElt cs e), R.Elt (Pixel cs e), Unbox (Pixel cs e)) data Image RP cs e = RPImage !(Image RD cs e) dims (RPImage img) = dims img {-# INLINE dims #-} makeImage !ix = suspendedComputeP . makeImage ix {-# INLINE makeImage #-} singleton = RPImage . singleton {-# INLINE singleton #-} map !f (RPImage img) = suspendedComputeP . map f $ img {-# INLINE map #-} imap !f (RPImage img) = suspendedComputeP . imap f $ img {-# INLINE imap #-} zipWith !f (RPImage img1) (RPImage img2) = suspendedComputeP . zipWith f img1 $ img2 {-# INLINE zipWith #-} izipWith !f (RPImage img1) (RPImage img2) = suspendedComputeP . izipWith f img1 $ img2 {-# INLINE izipWith #-} traverse (RPImage img) newDims = suspendedComputeP . traverse img newDims {-# INLINE traverse #-} traverse2 (RPImage img1) (RPImage img2) newDims = suspendedComputeP . traverse2 img1 img2 newDims {-# INLINE traverse2 #-} transpose (RPImage img) = suspendedComputeP . transpose $ img {-# INLINE transpose #-} backpermute !f !g (RPImage img) = suspendedComputeP $ backpermute f g img {-# INLINE backpermute #-} fromLists = RPImage . fromLists {-# INLINE fromLists #-} instance Array RS cs e => ManifestArray RS cs e where index (RSImage (RUImage arr)) (i, j) = R.index arr (Z :. i :. j) index (RSImage (RScalar px)) (_, _) = px index _ _ = _errorCompute {-# INLINE index #-} deepSeqImage (RSImage (RUImage arr)) = R.deepSeqArray arr deepSeqImage (RSImage (RScalar px)) = seq px deepSeqImage _ = _errorCompute {-# INLINE deepSeqImage #-} (|*|) i1@(RSImage img1) i2@(RSImage img2) = i1 `deepSeqImage` i2 `deepSeqImage` computeS (mult img1 img2) {-# INLINE (|*|) #-} fold !f !px0 (RSImage (RUImage arr)) = R.foldAllS f px0 $ arr fold !f !px0 (RSImage (RScalar px)) = f px0 px fold _ _ _ = _errorCompute {-# INLINE fold #-} eq (RSImage (RUImage arr1)) (RSImage (RUImage arr2)) = R.equalsS arr1 arr2 eq _ _ = _errorCompute {-# INLINE eq #-} instance Array RP cs e => ManifestArray RP cs e where index (RPImage (RUImage arr)) (i, j) = R.index arr (Z :. i :. j) index (RPImage (RScalar px)) (0, 0) = px index (RPImage (RScalar _)) (_, _) = error "Scalar Image can only be indexed at (0,0)." index _ _ = _errorCompute {-# INLINE index #-} deepSeqImage (RPImage (RUImage arr)) = R.deepSeqArray arr deepSeqImage (RPImage (RScalar px)) = seq px deepSeqImage _ = _errorCompute {-# INLINE deepSeqImage #-} (|*|) i1@(RPImage img1) i2@(RPImage img2) = i1 `deepSeqImage` i2 `deepSeqImage` suspendedComputeP (mult img1 img2) {-# INLINE (|*|) #-} fold !f !px0 (RPImage (RUImage arr)) = head . R.foldAllP f px0 $ arr fold !f !px0 (RPImage (RScalar px)) = f px0 px fold _ _ _ = _errorCompute {-# INLINE fold #-} eq (RPImage (RUImage arr1)) (RPImage (RUImage arr2)) = head $ R.equalsP arr1 arr2 eq _ _ = _errorCompute {-# INLINE eq #-} instance ManifestArray RS cs e => SequentialArray RS cs e where foldl !f !a = foldl f a . exchange VU {-# INLINE foldl #-} foldr !f !a = foldr f a . exchange VU {-# INLINE foldr #-} mapM !f img = liftM (exchange RS) (mapM f (exchange VU img)) {-# INLINE mapM #-} mapM_ !f img = mapM_ f (exchange VU img) {-# INLINE mapM_ #-} foldM !f !a = foldM f a . exchange VU {-# INLINE foldM #-} foldM_ !f !a = foldM_ f a . exchange VU {-# INLINE foldM_ #-} instance ManifestArray RS cs e => MutableArray RS cs e where data MImage st RS cs e = MRSImage !(MImage st VU cs e) mdims (MRSImage (mdims -> sz)) = sz {-# INLINE mdims #-} thaw img = liftM MRSImage (thaw (exchange VU img)) {-# INLINE thaw #-} freeze (MRSImage mimg) = liftM (exchange RS) (freeze mimg) {-# INLINE freeze #-} new sz = liftM MRSImage (new sz) {-# INLINE new #-} read (MRSImage mimg) = read mimg {-# INLINE read #-} write (MRSImage mimg) = write mimg {-# INLINE write #-} swap (MRSImage mimg) = swap mimg {-# INLINE swap #-} -- | O(1) - Delays manifest array. instance Exchangable RS RD where exchange _ (RSImage img) = img {-# INLINE exchange #-} -- | O(1) - Delays manifest array. instance Exchangable RP RD where exchange _ (RPImage img) = img {-# INLINE exchange #-} -- | Computes delayed array sequentially. instance Exchangable RD RS where exchange _ (RDImage arr) = RSImage . RUImage . R.computeS $ arr exchange _ img = RSImage img {-# INLINE exchange #-} -- | O(1) - Changes computation strategy. instance Exchangable RP RS where exchange _ (RPImage img) = RSImage img {-# INLINE exchange #-} -- | Computes delayed array in parallel. instance Exchangable RD RP where exchange _ (RDImage arr) = RPImage . RUImage . R.suspendedComputeP $ arr exchange _ img = RPImage img {-# INLINE exchange #-} -- | O(1) - Changes computation strategy. instance Exchangable RS RP where exchange _ (RSImage img) = RPImage img {-# INLINE exchange #-} -- | O(1) - Changes to Repa representation. instance Exchangable VU RS where exchange _ img@(dims -> (1, 1)) = singleton (toUnboxedVector img V.! 0) exchange _ img = RSImage . RUImage . R.fromUnboxed (tSh2 $ dims img) . toUnboxedVector $ img {-# INLINE exchange #-} -- | O(1) - Changes to Repa representation. instance Exchangable VU RP where exchange _ img@(dims -> (1, 1)) = singleton (toUnboxedVector img V.! 0) exchange _ img = RPImage . RUImage . R.fromUnboxed (tSh2 $ dims img) . toUnboxedVector $ img {-# INLINE exchange #-} -- | O(1) - Changes to Vector representation. instance Exchangable RS VU where exchange _ img@(RSImage (RUImage arr)) = fromUnboxedVector (dims img) (R.toUnboxed arr) exchange _ (RSImage (RScalar px)) = singleton px exchange _ _ = _errorCompute {-# INLINE exchange #-} -- | O(1) - Changes to Vector representation. instance Exchangable RP VU where exchange _ img@(RPImage (RUImage arr)) = fromUnboxedVector (dims img) (R.toUnboxed arr) exchange _ (RPImage (RScalar px)) = singleton px exchange _ _ = _errorCompute {-# INLINE exchange #-} -- | Computes an image in parallel and ensures that all elements are evaluated. computeP :: (Array arr cs e, Array RP cs e, Exchangable arr RP) => Image arr cs e -> Image RP cs e computeP !img = head $ do img' <- return $ exchange RP img img' `deepSeqImage` return img' {-# INLINE computeP #-} -- | Computes an image sequentially and ensures that all elements are evaluated. computeS :: (Array arr cs e, Array RS cs e, Exchangable arr RS) => Image arr cs e -> Image RS cs e computeS !img = head $ do img' <- return $ exchange RS img img' `deepSeqImage` return img' {-# INLINE computeS #-} -- | Delays an image, so further operations can be fused together. delay :: (ManifestArray arr cs e, Array RD cs e, Exchangable arr RD) => Image arr cs e -> Image RD cs e delay = exchange RD {-# INLINE delay #-} mult :: Array RD cs e => Image RD cs e -> Image RD cs e -> Image RD cs e mult img1@(RUImage arr1) img2@(RUImage arr2) = if n1 /= m2 then error ("Inner dimensions of multiplied images must be the same, but received: "++ show img1 ++" X "++ show img2) else RDImage . R.fromFunction (Z :. m1 :. n2) $ getPx where (Z :. m1 :. n1) = R.extent arr1 (Z :. m2 :. n2) = R.extent arr2 getPx (Z :. i :. j) = R.sumAllS (R.slice arr1 (R.Any :. (i :: Int) :. R.All) R.*^ R.slice arr2 (R.Any :. (j :: Int))) {-# INLINE getPx #-} mult _ _ = _errorCompute {-# INLINE mult #-} shT2 :: DIM2 -> (Int, Int) shT2 !(Z :. i :. j) = (i, j) {-# INLINE shT2 #-} tSh2 :: (Int, Int) -> DIM2 tSh2 !(i, j) = (Z :. i :. j) {-# INLINE tSh2 #-} suspendedComputeP :: Array RD cs e => Image RD cs e -> Image RP cs e suspendedComputeP (RDImage arr) = RPImage . RUImage . R.suspendedComputeP $ arr suspendedComputeP !img = RPImage img {-# INLINE suspendedComputeP #-} getDelayed :: Array RD cs e => Image RD cs e -> R.Array R.D DIM2 (Pixel cs e) getDelayed (RUImage arr) = R.delay arr getDelayed (RDImage arr) = arr getDelayed _ = error "Scalar image is not an array." {-# INLINE getDelayed #-} -- | Create an image from a 2D Repa delayed array. fromRepaArray :: R.Array R.D DIM2 (Pixel cs e) -> Image RD cs e fromRepaArray = RDImage -- | Retrieve an underlying Repa array from `RD` image type. toRepaArray :: (ColorSpace cs, Unbox (PixelElt cs e)) => Image RD cs e -> R.Array R.D DIM2 (Pixel cs e) toRepaArray (RUImage arr) = R.delay arr toRepaArray (RDImage arr) = arr toRepaArray (RScalar px) = R.fromFunction (Z :. 1 :. 1) $ const px _errorCompute :: t _errorCompute = error "Image should be computed at ths point. Please report this error" _errorScalarOp :: t _errorScalarOp = error "This operation is not allowed on scalar images." instance R.Elt Bit where touch (Bit w) = R.touch w {-# INLINE touch #-} zero = 0 {-# INLINE zero #-} one = 1 {-# INLINE one #-} instance (ColorSpace cs, R.Elt e, Num e) => R.Elt (Pixel cs e) where touch !px = P.mapM_ (R.touch . getPxCh px) (enumFrom (toEnum 0)) {-# INLINE touch #-} zero = 0 {-# INLINE zero #-} one = 1 {-# INLINE one #-}