module Codec.BMP
(
BMP (..)
, FileHeader (..)
, BitmapInfo (..)
, BitmapInfoV3 (..)
, BitmapInfoV4 (..)
, BitmapInfoV5 (..)
, Compression (..)
, CIEXYZ (..)
, Error (..)
, readBMP, hGetBMP, parseBMP
, writeBMP, hPutBMP, renderBMP
, packRGBA32ToBMP
, packRGBA32ToBMP32
, packRGBA32ToBMP24
, 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.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 = BSL.toStrict 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)