-------------------------------------------------------------------------------- -- Module : Data.Bitmap.IO.File -- Version : 0.0.2 -- License : BSD3 -- Copyright : (c) 2009-2010 Balazs Komuves -- Author : Balazs Komuves -- Maintainer : bkomuves (plus) hackage (at) gmail (dot) com -- Stability : experimental -- Portability : requires FFI and CPP -- Tested with : GHC 6.10.1 -------------------------------------------------------------------------------- -- | Saving and loading uncompressed bitmaps. -- For loading from compressed formats, see the @stb-image@ library: -- . -- -- The goal of this module is to provide the simplest possible interface -- for loading and saving bitmaps; so you can start experimenting -- without much hassle. -- -- Note: Endianness is the endianness of the host, so the resulting file is -- not portable across platforms with different endiannesses. -- {-# LANGUAGE CPP, ForeignFunctionInterface #-} module Data.Bitmap.IO.File ( readBitmap , writeBitmap -- , readRawData , writeRawData -- , hPutHeader , hPutRawData , hGetHeader , hGetRawData ) where -------------------------------------------------------------------------------- import Control.Monad import System.IO --import Foreign -- GHC 7 complains? import Foreign.C import Foreign.Ptr import Foreign.ForeignPtr import Foreign.Storable import Foreign.Marshal import Data.Int import Data.Bitmap.Base import Data.Bitmap.Internal import Data.Bitmap.IO -------------------------------------------------------------------------------- readBitmap :: PixelComponent t => FilePath -> IO (IOBitmap t) readBitmap fpath = do h <- openFile fpath ReadMode header <- hGetHeader h bitmap <- hGetRawData h header hClose h return bitmap readRawData :: PixelComponent t => FilePath -> (Size,NChn,PixelComponentType) -> IO (IOBitmap t) readRawData fpath header = do h <- openFile fpath ReadMode bitmap <- hGetRawData h header hClose h return bitmap -------------------------------------------------------------------------------- writeBitmap :: PixelComponent t => FilePath -> IOBitmap t -> IO () writeBitmap fpath bm = do h <- openFile fpath WriteMode hPutHeader h bm hPutRawData h bm hClose h -- | Saves only the raw pixel data, no resolution etc. writeRawData :: PixelComponent t => FilePath -> IOBitmap t -> IO () writeRawData fpath bm = do h <- openFile fpath WriteMode hPutRawData h bm hClose h -------------------------------------------------------------------------------- -- | Writes a 16 byte header in the following format: -- -- > dword xsize -- > dword ysize -- > dword nchn -- > dword pixelcomponent_type -- -- Pixel component encoding is the following: -- -- * 1 = Word8 -- -- * 2 = Word16 -- -- * 3 = Word32 -- -- * 4 = Float -- -- Endianness is the endianness of the host, so the resulting file is -- not portable across platform with different endiannesses. hPutHeader :: PixelComponent t => Handle -> IOBitmap t -> IO () hPutHeader h bm = do let (xsize,ysize) = bitmapSize bm nchn = bitmapNChannels bm typ = bitmapCType bm with (fromIntegral xsize :: Int32) $ \p -> hPutBuf h p 4 with (fromIntegral ysize :: Int32) $ \p -> hPutBuf h p 4 with (fromIntegral nchn :: Int32) $ \p -> hPutBuf h p 4 with (fromIntegral typ :: Int32) $ \p -> hPutBuf h p 4 -- | Saves only the raw pixel data, no resolution etc. hPutRawData :: PixelComponent t => Handle -> IOBitmap t -> IO () hPutRawData h bm = withIOBitmap bm $ \(xres,yres) nchn padding ptr -> do forM_ [0..yres-1] $ \k -> do let q = ptr `plusPtr` (k*long) hPutBuf h q short where long = bitmapPaddedRowSizeInBytes bm short = bitmapUnpaddedRowSizeInBytes bm -------------------------------------------------------------------------------- hGetRawData :: PixelComponent t => Handle -> (Size,NChn,PixelComponentType) -> IO (IOBitmap t) hGetRawData h (siz,nchn,pct) = do bm <- newIOBitmapUninitialized siz nchn (Just 1) if bitmapComponentType bm /= pct then error "bitmap/getRawData: bitmap component type does not match" else do withIOBitmap bm $ \(_,ysiz) _ _ ptr -> do let n = ysiz * bitmapUnpaddedRowSizeInBytes bm k <- hGetBuf h ptr n when (k/=n) $ error "bitmap/getRawData: not enough data" return bm hGetHeader :: Handle -> IO (Size,NChn,PixelComponentType) hGetHeader h = do xsize <- loadInt32 ysize <- loadInt32 nchn <- loadInt32 ctyp <- loadInt32 return ( (fromIntegral xsize, fromIntegral ysize) , fromIntegral nchn , decodeCType (fromIntegral ctyp) ) where loadInt32 :: IO Int32 loadInt32 = alloca $ \p -> do hGetBuf h p 4 peek p --------------------------------------------------------------------------------