{-# LANGUAGE PackageImports, PatternGuards, ExplicitForAll #-} -- | Reading and writing arrays as uncompressed 24 or 32 bit Windows BMP files. module Data.Array.Repa.IO.BMP ( readImageFromBMP , writeImageToBMP) where import Data.Array.Repa as R import Data.Array.Repa.Unsafe as R import Data.Array.Repa.Repr.ForeignPtr as R import Data.Array.Repa.Repr.ByteString as R import Data.Vector.Unboxed as U import Prelude as P import Foreign.ForeignPtr import Foreign.Marshal.Alloc import Data.ByteString.Unsafe as B import Codec.BMP import Data.Word -- NOTE: We set most of these functions as NOINLINE so it's easier to understand -- what's going on in the core programs. The top-level IO functions are -- only called a few times each, so it doesn't matter if they're not -- worker/wrappered etc. -- Read ------------------------------------------------------------------------------------------- -- | Read RGB components from a BMP file. readImageFromBMP :: FilePath -> IO (Either Error (Array U DIM2 (Word8, Word8, Word8))) {-# NOINLINE readImageFromBMP #-} readImageFromBMP filePath = do ebmp <- readBMP filePath case ebmp of Left err -> return $ Left err Right bmp -> do arr <- readImageFromBMP' bmp return $ Right arr readImageFromBMP' bmp = do let (width, height) = bmpDimensions bmp let arr = R.fromByteString (Z :. height :. width * 4) $ unpackBMPToRGBA32 bmp let shapeFn _ = Z :. height :. width -- Read out the components into their own arrays, -- skipping the alpha channel. vecRed <- computeP $ unsafeTraverse arr shapeFn (\get (sh :. x) -> get (sh :. (x * 4))) vecGreen <- computeP $ unsafeTraverse arr shapeFn (\get (sh :. x) -> get (sh :. (x * 4 + 1))) vecBlue <- computeP $ unsafeTraverse arr shapeFn (\get (sh :. x) -> get (sh :. (x * 4 + 2))) -- O(1). zip the components together let vecRGB = U.zip3 (toUnboxed vecRed) (toUnboxed vecGreen) (toUnboxed vecBlue) return $ fromUnboxed (Z :. height :. width) vecRGB -- | Write RGB components to a BMP file. writeImageToBMP :: FilePath -> Array U DIM2 (Word8, Word8, Word8) -> IO () {-# NOINLINE writeImageToBMP #-} writeImageToBMP fileName arrRGB = do let sh@(Z :. height :. width) = extent arrRGB -- O(1). unzip the components let (vecRed, vecGreen, vecBlue) = U.unzip3 $ toUnboxed arrRGB -- Create a bytestring with all the data ptr <- mallocBytes (height * width * 4) fptr <- newForeignPtr finalizerFree ptr computeIntoP fptr $ interleave4 (fromUnboxed sh vecRed) (fromUnboxed sh vecGreen) (fromUnboxed sh vecBlue) (fromFunction sh (\_ -> 255)) -- Pack the data into a BMP file and write it out. withForeignPtr fptr $ \ptr' -> do bs <- unsafePackCStringFinalizer ptr' (width * height * 4) (return ()) let bmp = packRGBA32ToBMP width height bs writeBMP fileName bmp {- -- Normalise -------------------------------------------------------------------------------------- -- | Normalise a matrix to to [0 .. 1], discarding negative values. -- If the maximum value is 0 then return the array unchanged. normalisePositive01 :: (Shape sh, Fractional a, Ord a) => Array sh a -> Array sh a {-# INLINE normalisePositive01 #-} normalisePositive01 arr = let mx = foldAll max 0 arr elemFn x | x >= 0 = x / mx | otherwise = x in mx `seq` if mx == 0 then arr else R.map elemFn arr -}