module Codec.BMP
(
BMP (..)
, FileHeader (..)
, BitmapInfo (..)
, BitmapInfoV3 (..)
, BitmapInfoV4 (..)
, BitmapInfoV5 (..)
, Compression (..)
, CIEXYZ (..)
, Error (..)
, readBMP, hGetBMP, parseBMP
, writeBMP, hPutBMP, renderBMP
, packRGBA32ToBMP
, unpackBMPToRGBA32
, bmpDimensions)
where
import Codec.BMP.Base
import Codec.BMP.Error
import Codec.BMP.Unpack
import Codec.BMP.Pack
import Codec.BMP.FileHeader
import Codec.BMP.BitmapInfo
import Codec.BMP.BitmapInfoV3
import Codec.BMP.BitmapInfoV4
import Codec.BMP.BitmapInfoV5
import System.IO
import Data.ByteString as BS
import Data.ByteString.Lazy as BSL
import Data.Binary
import Data.Binary.Get
readBMP :: FilePath -> IO (Either Error BMP)
readBMP fileName
= do h <- openBinaryFile fileName ReadMode
hGetBMP h
hGetBMP :: Handle -> IO (Either Error BMP)
hGetBMP h
= do
buf <- BSL.hGetContents h
return $ parseBMP buf
parseBMP :: BSL.ByteString -> Either Error BMP
parseBMP buf
= let
(bufFileHeader, bufRest)
= BSL.splitAt (fromIntegral sizeOfFileHeader) buf
in if (fromIntegral $ BSL.length bufFileHeader) /= sizeOfFileHeader
then Left ErrorFileHeaderTruncated
else parseBMP2 bufRest (decode bufFileHeader)
parseBMP2 buf fileHeader
| fileHeaderType fileHeader /= bmpMagic
= Left $ ErrorBadMagic (fileHeaderType fileHeader)
| otherwise
= let
sizeHeader = runGet getWord32le buf
(bufImageHeader, bufRest)
= BSL.splitAt (fromIntegral sizeHeader) buf
physicalBufferSize
= (fromIntegral $ BSL.length bufRest) :: Word32
in if (fromIntegral $ BSL.length bufImageHeader) /= sizeHeader
then Left ErrorImageHeaderTruncated
else parseBMP3 fileHeader bufImageHeader bufRest physicalBufferSize
parseBMP3 fileHeader bufImageHeader bufRest physicalBufferSize
| BSL.length bufImageHeader == 40
= let info = decode bufImageHeader
in case checkBitmapInfoV3 info physicalBufferSize of
Just err -> Left err
Nothing
| Just imageSize <- imageSizeFromBitmapInfoV3 info
-> parseBMP4 fileHeader (InfoV3 info) bufRest imageSize
| otherwise
-> Left $ ErrorInternalErrorPleaseReport
| BSL.length bufImageHeader == 108
= let info = decode bufImageHeader
in case checkBitmapInfoV4 info physicalBufferSize of
Just err -> Left err
Nothing
| Just imageSize <- imageSizeFromBitmapInfoV4 info
-> parseBMP4 fileHeader (InfoV4 info) bufRest imageSize
| otherwise
-> Left $ ErrorInternalErrorPleaseReport
| BSL.length bufImageHeader == 124
= let info = decode bufImageHeader
in case checkBitmapInfoV5 info physicalBufferSize of
Just err -> Left err
Nothing
| Just imageSize <- imageSizeFromBitmapInfoV5 info
-> parseBMP4 fileHeader (InfoV5 info) bufRest imageSize
| otherwise
-> Left $ ErrorInternalErrorPleaseReport
| otherwise
= Left $ ErrorUnhandledBitmapHeaderSize
$ fromIntegral $ BSL.length bufImageHeader
parseBMP4 fileHeader imageHeader bufImage (sizeImage :: Int)
= let bufLen = fromIntegral $ BSL.length bufImage
in if bufLen < sizeImage
then Left $ ErrorImageDataTruncated sizeImage bufLen
else Right $ BMP
{ bmpFileHeader = fileHeader
, bmpBitmapInfo = imageHeader
, bmpRawImageData = BS.concat $ BSL.toChunks bufImage }
writeBMP :: FilePath -> BMP -> IO ()
writeBMP fileName bmp
= do h <- openBinaryFile fileName WriteMode
hPutBMP h bmp
hFlush h
hClose h
hPutBMP :: Handle -> BMP -> IO ()
hPutBMP h bmp
= BSL.hPut h (renderBMP bmp)
renderBMP :: BMP -> BSL.ByteString
renderBMP bmp
= BSL.append (encode $ bmpFileHeader bmp)
$ BSL.append (encode $ bmpBitmapInfo bmp)
$ BSL.fromStrict (bmpRawImageData bmp)
bmpDimensions :: BMP -> (Int, Int)
bmpDimensions bmp
= let info = getBitmapInfoV3 $ bmpBitmapInfo bmp
in ( fromIntegral $ dib3Width info
, fromIntegral $ dib3Height info)