module Util ( bitmapLoad, Image(..) ) where import Data.Word ( Word8, Word16 ) import Data.Int ( Int32 ) import Foreign ( Ptr, allocaBytes, pokeElemOff, peekElemOff, peek, castPtr, Storable, mallocBytes, free, copyBytes ) import Graphics.Rendering.OpenGL ( PixelData(..), PixelFormat(..), Size(..), DataType(..) ) import System.IO ( IOMode( ReadMode ), openBinaryFile, hSeek, SeekMode( RelativeSeek ), Handle, hGetBuf ) import System.IO.Unsafe ( unsafePerformIO ) data Endian = LittleEndian | BigEndian deriving (Eq, Ord, Show) data Image = Image Size (PixelData Word8) bitmapLoad :: String -> IO Image bitmapLoad f = do handle <- openBinaryFile f ReadMode hSeek handle RelativeSeek 18 width <- readInt handle putStrLn ("Width of "++f++": "++show width) height <- readInt handle putStrLn ("Height of "++f++": "++show height) planes <- readShort handle bpp <- readShort handle size <- return (width*height*(fromIntegral bpp `div` 8)) hSeek handle RelativeSeek 24 putStrLn ("Planes = "++(show planes)) bgrBytes <- (readBytes handle (fromIntegral size) :: IO (Ptr Word8)) rgbBytes <- bgr2rgb bgrBytes (fromIntegral size) return (Image (Size (fromIntegral width) (fromIntegral height)) (PixelData RGB UnsignedByte rgbBytes)) endian :: Endian endian = let r = unsafePerformIO ( do w <- allocaBytes 4 (\p -> do pokeElemOff p 0 (0::Word8) pokeElemOff p 1 (1::Word8) pokeElemOff p 2 (2::Word8) pokeElemOff p 3 (3::Word8) peek (castPtr p) :: IO Int32) return w) in case r of 50462976 -> LittleEndian 66051 -> BigEndian _ -> undefined bgr2rgb :: Ptr Word8 -> Int -> IO (Ptr Word8) bgr2rgb p n = mapM_ (\i -> do b <- peekElemOff p (i+0) g <- peekElemOff p (i+1) r <- peekElemOff p (i+2) pokeElemOff p (i+0) r pokeElemOff p (i+1) g pokeElemOff p (i+2) b) [0,3..n-3] >> return p -- This is only needed if you're on PowerPC instead of x86 -- if you are on x86 use the following: -- reverseBytes p _ = return p reverseBytes :: Ptr Word8 -> Int -> IO (Ptr Word8) reverseBytes p n | endian == BigEndian = do p' <- mallocBytes n mapM_ (\i -> peekElemOff p i >>= pokeElemOff p' (n-i-1)) [0..n-1] return p' | endian == LittleEndian = do p' <- mallocBytes n copyBytes p' p n return p' reverseBytes _ _ = undefined readBytes :: Storable a => Handle -> Int -> IO (Ptr a) readBytes h n = do p <- mallocBytes n hGetBuf h p n return p readShort :: Handle -> IO Word16 readShort h = do p <- readBytes h 2 :: IO (Ptr Word8) p' <- reverseBytes (castPtr p) 2 free p r <- peek (castPtr p') free p' return r readInt :: Handle -> IO Int32 readInt h = do p <- readBytes h 4 :: IO (Ptr Word8) p' <- reverseBytes (castPtr p) 4 free p r <- peek (castPtr p') free p' return r