Commit b6c1feee authored by dterei's avatar dterei

Update Repa libraries (fixes Blur for now)

parent 0ba9cc19
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
module Fibon.Benchmarks.Repa.Blur.Fibon.Instance(
mkInstance
)
where
import Fibon.BenchmarkInstance
sharedConfig = BenchmarkInstance {
flagConfig = FlagConfig {
configureFlags = ["--ghc-option=-threaded"]
, buildFlags = []
, runFlags = []
}
, stdinInput = Nothing
, output = [(OutputFile "out.bmp", Diff "out.expected.bmp")]
, exeName = "repa-blur"
}
flgCfg = flagConfig sharedConfig
mkInstance Test = sharedConfig {
flagConfig = flgCfg {runFlags = words "1 step20.bmp out.bmp"}
}
mkInstance Ref = sharedConfig {
flagConfig = flgCfg {runFlags = words "12 lena.bmp out.bmp"}
}
TOP = ../../..
include $(TOP)/mk/boilerplate.mk
SRCS = ../_RepaLib/bmp/Codec/BMP/CIEXYZ.hs \
../_RepaLib/bmp/Codec/BMP/Error.hs \
SRCS = ../_RepaLib/bmp/Codec/BMP/Base.hs \
../_RepaLib/bmp/Codec/BMP/BitmapInfo.hs \
../_RepaLib/bmp/Codec/BMP/BitmapInfoV3.hs \
../_RepaLib/bmp/Codec/BMP/FileHeader.hs \
../_RepaLib/bmp/Codec/BMP/BitmapInfoV4.hs \
../_RepaLib/bmp/Codec/BMP/BitmapInfoV5.hs \
../_RepaLib/bmp/Codec/BMP/BitmapInfo.hs \
../_RepaLib/bmp/Codec/BMP/Base.hs \
../_RepaLib/bmp/Codec/BMP/Unpack.hs \
../_RepaLib/bmp/Codec/BMP/Pack.hs \
../_RepaLib/bmp/Codec/BMP/CIEXYZ.hs \
../_RepaLib/bmp/Codec/BMP/Compression.hs \
../_RepaLib/bmp/Codec/BMP/Error.hs \
../_RepaLib/bmp/Codec/BMP/FileHeader.hs \
../_RepaLib/bmp/Codec/BMP.hs \
../_RepaLib/bmp/Codec/BMP/Pack.hs \
../_RepaLib/bmp/Codec/BMP/Unpack.hs \
../_RepaLib/quickcheck/Test/QuickCheck/All.hs \
../_RepaLib/quickcheck/Test/QuickCheck/Arbitrary.hs \
../_RepaLib/quickcheck/Test/QuickCheck/Exception.hs \
../_RepaLib/quickcheck/Test/QuickCheck/Text.hs \
../_RepaLib/quickcheck/Test/QuickCheck/State.hs \
../_RepaLib/quickcheck/Test/QuickCheck/Function.hs \
../_RepaLib/quickcheck/Test/QuickCheck/Gen.hs \
../_RepaLib/quickcheck/Test/QuickCheck/Arbitrary.hs \
../_RepaLib/quickcheck/Test/QuickCheck.hs \
../_RepaLib/quickcheck/Test/QuickCheck/Modifiers.hs \
../_RepaLib/quickcheck/Test/QuickCheck/Monadic.hs \
../_RepaLib/quickcheck/Test/QuickCheck/Poly.hs \
../_RepaLib/quickcheck/Test/QuickCheck/Property.hs \
../_RepaLib/quickcheck/Test/QuickCheck/State.hs \
../_RepaLib/quickcheck/Test/QuickCheck/Test.hs \
../_RepaLib/quickcheck/Test/QuickCheck.hs \
../_RepaLib/repa/Data/Array/Repa/QuickCheck.hs \
../_RepaLib/repa/Data/Array/Repa/Shape.hs \
../_RepaLib/quickcheck/Test/QuickCheck/Text.hs \
../_RepaLib/repa-algorithms/Data/Array/Repa/Algorithms/Complex.hs \
../_RepaLib/repa-algorithms/Data/Array/Repa/Algorithms/Convolve.hs \
../_RepaLib/repa-algorithms/Data/Array/Repa/Algorithms/DFT/Center.hs \
../_RepaLib/repa-algorithms/Data/Array/Repa/Algorithms/DFT.hs \
../_RepaLib/repa-algorithms/Data/Array/Repa/Algorithms/DFT/Roots.hs \
../_RepaLib/repa-algorithms/Data/Array/Repa/Algorithms/FFT.hs \
../_RepaLib/repa-algorithms/Data/Array/Repa/Algorithms/Iterate.hs \
../_RepaLib/repa-algorithms/Data/Array/Repa/Algorithms/Matrix.hs \
../_RepaLib/repa-algorithms/Data/Array/Repa/Algorithms/Randomish.hs \
../_RepaLib/repa-bytestring/Data/Array/Repa/ByteString.hs \
../_RepaLib/repa/Data/Array/Repa/Arbitrary.hs \
../_RepaLib/repa/Data/Array/Repa.hs \
../_RepaLib/repa/Data/Array/Repa/Index.hs \
../_RepaLib/repa/Data/Array/Repa/Internals/Base.hs \
../_RepaLib/repa/Data/Array/Repa/Internals/Elt.hs \
../_RepaLib/repa/Data/Array/Repa/Internals/EvalBlockwise.hs \
../_RepaLib/repa/Data/Array/Repa/Internals/EvalChunked.hs \
../_RepaLib/repa/Data/Array/Repa/Internals/EvalCursored.hs \
../_RepaLib/repa/Data/Array/Repa/Internals/EvalReduction.hs \
../_RepaLib/repa/Data/Array/Repa/Internals/Forcing.hs \
../_RepaLib/repa/Data/Array/Repa/Internals/Gang.hs \
../_RepaLib/repa/Data/Array/Repa/Internals/Select.hs \
../_RepaLib/repa/Data/Array/Repa/Operators/IndexSpace.hs \
../_RepaLib/repa/Data/Array/Repa/Operators/Interleave.hs \
../_RepaLib/repa/Data/Array/Repa/Operators/Mapping.hs \
../_RepaLib/repa/Data/Array/Repa/Operators/Modify.hs \
../_RepaLib/repa/Data/Array/Repa/Operators/Reduction.hs \
../_RepaLib/repa/Data/Array/Repa/Operators/Select.hs \
../_RepaLib/repa/Data/Array/Repa/Operators/Traverse.hs \
../_RepaLib/repa/Data/Array/Repa/Properties.hs \
../_RepaLib/repa/Data/Array/Repa/Shape.hs \
../_RepaLib/repa/Data/Array/Repa/Slice.hs \
../_RepaLib/repa/Data/Array/Repa.hs \
../_RepaLib/repa-bytestring/Data/Array/Repa/ByteString.hs \
../_RepaLib/repa-algorithms/Data/Array/Repa/Algorithms/Convolve.hs \
../_RepaLib/repa-io/Data/Array/Repa/IO/Timing.hs \
../_RepaLib/repa/Data/Array/Repa/Specialised/Dim2.hs \
../_RepaLib/repa/Data/Array/Repa/Stencil/Base.hs \
../_RepaLib/repa/Data/Array/Repa/Stencil.hs \
../_RepaLib/repa/Data/Array/Repa/Stencil/Template.hs \
../_RepaLib/repa-io/Data/Array/Repa/IO/Binary.hs \
../_RepaLib/repa-io/Data/Array/Repa/IO/BMP.hs \
../_RepaLib/repa-io/Data/Array/Repa/IO/ColorRamp.hs \
../_RepaLib/repa-io/Data/Array/Repa/IO/Internals/Text.hs \
../_RepaLib/repa-io/Data/Array/Repa/IO/Matrix.hs \
../_RepaLib/repa-io/Data/Array/Repa/IO/Timing.hs \
../_RepaLib/repa-io/Data/Array/Repa/IO/Vector.hs \
src/Main.hs
PROG_ARGS += 12 lena.bmp out.bmp
HC_OPTS += -threaded -i. -i../_RepaLib/repa -i../_RepaLib/repa-algorithms -i../_RepaLib/repa-io -i../_RepaLib/bmp -i../_RepaLib/repa-bytestring -i../_RepaLib/quickcheck -package base -package binary -package bytestring -package dph-base -package dph-prim-par -package dph-prim-seq -package extensible-exceptions -package ghc -package mtl -package old-time -package random -package vector
CLEAN_FILES += out.bmp
include $(TOP)/mk/target.mk
{-# LANGUAGE PackageImports, BangPatterns #-}
{-# LANGUAGE PackageImports, BangPatterns, TemplateHaskell, QuasiQuotes #-}
{-# OPTIONS -Wall -fno-warn-missing-signatures -fno-warn-incomplete-patterns #-}
import Data.List
import Control.Monad
import System.Environment
import Data.Word
import Data.Array.Repa.IO.BMP
import Data.Array.Repa.IO.Timing
import Data.Array.Repa.Algorithms.Convolve
import Data.Array.Repa.Algorithms.Iterate
import Data.Array.Repa as A
import Data.Array.Repa.Stencil as A
import Prelude as P
main
......@@ -17,53 +19,71 @@ main
_ -> usage
usage = putStr $ unlines
[ "repa-blur <iterations> <fileIn.bmp> <fileOut.bmp>" ]
[ "repa-blur <iterations::Int> <fileIn.bmp> <fileOut.bmp>" ]
-- TODO: component-wise is filthy.
-- do this with a DIM3 array.
run iterations fileIn fileOut
= do (arrRed, arrGreen, arrBlue)
<- liftM (either (error . show) id)
$ readComponentsFromBMP fileIn
= do comps <- liftM (either (error . show) id)
$ readComponentsListFromBMP fileIn
arrRed `deepSeqArray` arrGreen `deepSeqArray` arrBlue `deepSeqArray` return ()
((arrRed', arrGreen', arrBlue'), tElapsed)
<- time $ let result = blurComponents iterations arrRed arrGreen arrBlue
in result `seq` return result
comps `deepSeqArrays` return ()
(comps', _)
<- time $ let comps' = P.map (process iterations) comps
in comps' `deepSeqArrays` return comps'
-- putStr $ prettyTime tElapsed
putStrLn "Done"
putStrLn "Done"
writeComponentsToBMP fileOut arrRed' arrGreen' arrBlue'
writeComponentsListToBMP fileOut comps'
{-# NOINLINE process #-}
process :: Int -> Array DIM2 Word8 -> Array DIM2 Word8
process iterations = demote . blur iterations . promote
{-# NOINLINE promote #-}
promote :: Array DIM2 Word8 -> Array DIM2 Double
promote arr@(Array _ [Region RangeAll (GenManifest _)])
= arr `deepSeqArray` force
$ A.map ffs arr
-- | Blur all the components of an image.
blurComponents iterations arrRed arrGreen arrBlue
= let process arr
= force
$ A.map truncate
$ blurs iterations
$ force
$ A.map fromIntegral
$ arr
where {-# INLINE ffs #-}
ffs :: Word8 -> Double
ffs x = fromIntegral (fromIntegral x :: Int)
[arrRed', arrGreen', arrBlue']
= P.map process [arrRed, arrGreen, arrBlue]
in arrRed' `deepSeqArray` arrGreen' `deepSeqArray` arrBlue' `deepSeqArray`
(arrRed', arrGreen', arrBlue')
{-# NOINLINE demote #-}
demote :: Array DIM2 Double -> Array DIM2 Word8
demote arr@(Array _ [Region RangeAll (GenManifest _)])
= arr `deepSeqArray` force
$ A.map ffs arr
-- | Run several iterations of blurring.
blurs :: Int -> Array DIM2 Double -> Array DIM2 Double
blurs 0 arr = arr
blurs n arr = blurs (n - 1) (force $ blur arr)
where {-# INLINE ffs #-}
ffs :: Double -> Word8
ffs x = fromIntegral (truncate x :: Int)
-- | Run a single iteration of blurring.
{-# NOINLINE blur #-}
blur :: Int -> Array DIM2 Double -> Array DIM2 Double
blur !iterations arr@(Array _ [Region RangeAll (GenManifest _)])
= arr `deepSeqArray` force2
$ iterateBlockwise' iterations arr
$ A.map (/ 159)
. mapStencil2 BoundClamp
[stencil2| 2 4 5 4 2
4 9 12 9 4
5 12 15 12 5
4 9 12 9 4
2 4 5 4 2 |]
{- version using convolveOut
-- | Run several iterations of blurring.
blur :: Int -> Array DIM2 Double -> Array DIM2 Double
blur 0 arr = arr
blur n arr = blurs (n - 1) (force $ blur arr)
-- | Run a single iteration of blurring.
blur :: Array DIM2 Double -> Array DIM2 Double
blur input@Manifest{}
= convolveOut outClamp kernel input
......@@ -75,5 +95,5 @@ blur input@Manifest{}
5.0, 12.0, 15.0, 12.0, 5.0,
4.0, 9.0, 12.0, 9.0, 4.0,
2.0, 4.0, 5.0, 4.0, 2.0]
-}
......@@ -2,7 +2,8 @@
-- | Reading and writing uncompressed BMP files.
--
-- Reading works for both uncompressed 24bit RGB WindowsV3 and 32bit RGBA WindowsV4 formats.
-- Reading works for both uncompressed 24bit RGB and 32bit RGBA
-- WindowsV3, WindowsV4 and WindowsV5 formats.
--
-- Writing is limited to the uncompressed 24bit RGB WindowsV3 format.
--
......@@ -22,7 +23,14 @@
-- > let (width, height) = bmpDimensions bmp
-- > ...
--
-- Release Notes:
--
-- > * bmp 1.2.0
-- > Accept files with zero padding on the end of the file.
-- > Accept RGBA files with V3 headers.
--
-- > * bmp 1.1.2
-- > Accept files with the image size field set to zero.
--
module Codec.BMP
( BMP (..)
......@@ -77,14 +85,12 @@ hGetBMP h
= BSL.splitAt (fromIntegral sizeOfFileHeader) buf
if (fromIntegral $ BSL.length bufFileHeader) /= sizeOfFileHeader
then return $ Left ErrorReadOfFileHeaderFailed
then return $ Left ErrorFileHeaderTruncated
else hGetBMP2 bufRest (decode bufFileHeader)
hGetBMP2 buf fileHeader
-- Check the magic before doing anything else.
-- If the specified file is not a BMP file then we'd prefer to get
-- this error than a `ReadOfImageHeaderFailed`.
| fileHeaderType fileHeader /= bmpMagic
= return $ Left $ ErrorBadMagic (fileHeaderType fileHeader)
......@@ -96,48 +102,59 @@ hGetBMP2 buf fileHeader
-- split off the image header
let (bufImageHeader, bufRest)
= BSL.splitAt (fromIntegral sizeHeader) buf
-- How much non-header data is present in the file.
-- For uncompressed data without a colour table, the remaining data should
-- be the image, but there may also be padding bytes on the end.
let physicalBufferSize
= (fromIntegral $ BSL.length bufRest) :: Word32
if (fromIntegral $ BSL.length bufImageHeader) /= sizeHeader
then return $ Left ErrorReadOfImageHeaderFailed
else hGetBMP3 fileHeader bufImageHeader bufRest
then return $ Left ErrorImageHeaderTruncated
else hGetBMP3 fileHeader bufImageHeader bufRest physicalBufferSize
hGetBMP3 fileHeader bufImageHeader bufRest
hGetBMP3 fileHeader bufImageHeader bufRest physicalBufferSize
| BSL.length bufImageHeader == 40
= do let info = decode bufImageHeader
case checkBitmapInfoV3 info of
case checkBitmapInfoV3 info physicalBufferSize of
Just err -> return $ Left err
Nothing -> hGetBMP4 fileHeader (InfoV3 info) bufRest
(fromIntegral $ dib3ImageSize info)
Nothing
| Just imageSize <- imageSizeFromBitmapInfoV3 info
-> hGetBMP4 fileHeader (InfoV3 info) bufRest imageSize
| otherwise
-> return $ Left $ ErrorInternalErrorPleaseReport
| BSL.length bufImageHeader == 108
= do let info = decode bufImageHeader
case checkBitmapInfoV4 info of
case checkBitmapInfoV4 info physicalBufferSize of
Just err -> return $ Left err
Nothing -> hGetBMP4 fileHeader (InfoV4 info) bufRest
(fromIntegral
$ dib3ImageSize
$ dib4InfoV3 info)
Nothing
| Just imageSize <- imageSizeFromBitmapInfoV4 info
-> hGetBMP4 fileHeader (InfoV4 info) bufRest imageSize
| otherwise
-> return $ Left $ ErrorInternalErrorPleaseReport
| BSL.length bufImageHeader == 124
= do let info = decode bufImageHeader
case checkBitmapInfoV5 info of
case checkBitmapInfoV5 info physicalBufferSize of
Just err -> return $ Left err
Nothing -> hGetBMP4 fileHeader (InfoV5 info) bufRest
(fromIntegral
$ dib3ImageSize
$ dib4InfoV3
$ dib5InfoV4 info)
Nothing
| Just imageSize <- imageSizeFromBitmapInfoV5 info
-> hGetBMP4 fileHeader (InfoV5 info) bufRest imageSize
| otherwise
-> return $ Left $ ErrorInternalErrorPleaseReport
| otherwise
= return
$ Left
$ ErrorUnhandledBitmapHeaderSize (fromIntegral $ BSL.length bufImageHeader)
= return $ Left
$ ErrorUnhandledBitmapHeaderSize
$ fromIntegral $ BSL.length bufImageHeader
hGetBMP4 fileHeader imageHeader bufImage (sizeImage :: Int)
= if (fromIntegral $ BSL.length bufImage) /= sizeImage
then return $ Left ErrorReadOfImageDataFailed
= let bufLen = fromIntegral $ BSL.length bufImage
in if bufLen < sizeImage
then return $ Left $ ErrorImageDataTruncated sizeImage bufLen
else return
$ Right $ BMP
{ bmpFileHeader = fileHeader
......@@ -152,6 +169,7 @@ writeBMP fileName bmp
= do h <- openBinaryFile fileName WriteMode
hPutBMP h bmp
hFlush h
hClose h
-- | Put a BMP image to a file handle.
......
{-# OPTIONS_HADDOCK hide #-}
module Codec.BMP.Base
(BMP (..))
( BMP (..))
where
import Codec.BMP.FileHeader
import Codec.BMP.BitmapInfo
import Data.ByteString
-- | A BMP image.
-- For an uncompressed image, the image data contains triples of BGR component values.
-- Each line may also have zero pad values on the end, to bring them up to a multiple
......@@ -18,6 +19,3 @@ data BMP
deriving Show
......@@ -18,6 +18,7 @@ data BitmapInfo
| InfoV5 BitmapInfoV5
deriving (Show)
instance Binary BitmapInfo where
get
= do size <- lookAhead getWord32le
......
......@@ -4,13 +4,16 @@ module Codec.BMP.BitmapInfoV3
( BitmapInfoV3 (..)
, Compression (..)
, sizeOfBitmapInfoV3
, checkBitmapInfoV3)
, checkBitmapInfoV3
, imageSizeFromBitmapInfoV3)
where
import Codec.BMP.Error
import Codec.BMP.Compression
import Data.Binary
import Data.Binary.Get
import Data.Binary.Put
-- | Device Independent Bitmap (DIB) header for Windows V3.
data BitmapInfoV3
= BitmapInfoV3
......@@ -33,6 +36,10 @@ data BitmapInfoV3
, dib3Compression :: Compression
-- | (+20) Size of raw image data.
-- Some encoders set this to zero, so we need to calculate it based on the
-- overall file size.
--
-- If it is non-zero then we check it matches the file size - header size.
, dib3ImageSize :: Word32
-- | (+24) Prefered resolution in pixels per meter, along the X axis.
......@@ -49,16 +56,6 @@ data BitmapInfoV3
}
deriving (Show)
data Compression
= CompressionRGB
| CompressionRLE8
| CompressionRLE4
| CompressionBitFields
| CompressionJPEG
| CompressionPNG
| CompressionUnknown Word32
deriving (Show, Eq)
-- | Size of `BitmapInfoV3` header (in bytes)
sizeOfBitmapInfoV3 :: Int
......@@ -105,53 +102,70 @@ instance Binary BitmapInfoV3 where
putWord32le $ dib3ColorsUsed header
putWord32le $ dib3ColorsImportant header
instance Binary Compression where
get
= do c <- getWord32le
case c of
0 -> return $ CompressionRGB
1 -> return $ CompressionRLE8
2 -> return $ CompressionRLE4
3 -> return $ CompressionBitFields
4 -> return $ CompressionJPEG
5 -> return $ CompressionPNG
_ -> return $ CompressionUnknown c
put c
= case c of
CompressionRGB -> putWord32le 0
CompressionRLE8 -> putWord32le 1
CompressionRLE4 -> putWord32le 2
CompressionBitFields -> putWord32le 3
CompressionJPEG -> putWord32le 4
CompressionPNG -> putWord32le 5
CompressionUnknown x -> putWord32le x
-- | Check headers for problems and unsupported features.
-- With a V3 header we only support the uncompressed 24bit RGB format.
checkBitmapInfoV3 :: BitmapInfoV3 -> Maybe Error
checkBitmapInfoV3 header
-- | Check headers for problems and unsupported features.
checkBitmapInfoV3 :: BitmapInfoV3 -> Word32 -> Maybe Error
checkBitmapInfoV3 header physicalBufferSize
-- We only handle a single color plane.
| dib3Planes header /= 1
= Just $ ErrorUnhandledPlanesCount
$ fromIntegral $ dib3Planes header
| dib3ImageSize header == 0
= Just $ ErrorZeroImageSize
= Just $ ErrorUnhandledPlanesCount $ dib3Planes header
| dib3ImageSize header `mod` dib3Height header /= 0
= Just $ ErrorLacksWholeNumberOfLines
-- We only handle 24 and 32 bit images.
| dib3BitCount header /= 24
= Just $ ErrorUnhandledColorDepth
$ fromIntegral $ dib3BitCount header
, dib3BitCount header /= 32
= Just $ ErrorUnhandledColorDepth $ dib3BitCount header
-- If the image size field in the header is non-zero,
-- then it must be the same as the physical size of the image buffer.
| headerImageSize <- dib3ImageSize header
, headerImageSize /= 0
, fromIntegral headerImageSize /= physicalBufferSize
= Just $ ErrorImagePhysicalSizeMismatch
headerImageSize physicalBufferSize
-- Check that the physical buffer contains enough image data.
-- It may contain more, as some encoders put padding bytes
-- on the end.
| Just calculatedImageSize <- imageSizeFromBitmapInfoV3 header
, fromIntegral physicalBufferSize < calculatedImageSize
= Just $ ErrorImageDataTruncated
calculatedImageSize
(fromIntegral physicalBufferSize)
-- We only handle uncompresssed images.
| dib3Compression header /= CompressionRGB
= Just $ ErrorUnhandledCompressionMode (dib3Compression header)
| dib3Compression header /= CompressionRGB
= Just $ ErrorUnhandledCompressionMode
| otherwise
= Nothing
-- | Compute the size of the image data from the header.
--
-- * We can't just use the 'dib3ImageSize' field because some encoders
-- set this to zero.
--
-- * We also can't use the physical size of the data in the file because
-- some encoders add zero padding bytes on the end.
--
imageSizeFromBitmapInfoV3 :: BitmapInfoV3 -> Maybe Int
imageSizeFromBitmapInfoV3 header
| dib3BitCount header == 32
, dib3Planes header == 1
, dib3Compression header == CompressionRGB
= Just $ fromIntegral (dib3Width header * dib3Height header * 4)
| dib3BitCount header == 24
, dib3Planes header == 1
, dib3Compression header == CompressionRGB
= let imageBytesPerLine = dib3Width header * 3
tailBytesPerLine = imageBytesPerLine `mod` 4
padBytesPerLine = if tailBytesPerLine > 0
then 4 - tailBytesPerLine
else 0
in Just $ fromIntegral (dib3Height header * imageBytesPerLine + padBytesPerLine)
| otherwise
= Nothing
......@@ -4,7 +4,8 @@ module Codec.BMP.BitmapInfoV4
( BitmapInfoV4 (..)
, CIEXYZ (..)
, sizeOfBitmapInfoV4
, checkBitmapInfoV4)
, checkBitmapInfoV4
, imageSizeFromBitmapInfoV4)
where
import Codec.BMP.Error
import Codec.BMP.CIEXYZ
......@@ -92,27 +93,44 @@ instance Binary BitmapInfoV4 where
-- With a V4 header we support both the uncompressed 24bit RGB format,
-- and the uncompressed 32bit RGBA format.
--
checkBitmapInfoV4 :: BitmapInfoV4 -> Maybe Error
checkBitmapInfoV4 headerV4
checkBitmapInfoV4 :: BitmapInfoV4 -> Word32 -> Maybe Error
checkBitmapInfoV4 headerV4 physicalBufferSize
-- We only handle a single color plane.
| dib3Planes headerV3 /= 1
= Just $ ErrorUnhandledPlanesCount
$ fromIntegral $ dib3Planes headerV3
| dib3ImageSize headerV3 == 0
= Just $ ErrorZeroImageSize
| dib3ImageSize headerV3 `mod` dib3Height headerV3 /= 0
= Just $ ErrorLacksWholeNumberOfLines
= Just $ ErrorUnhandledPlanesCount $ dib3Planes headerV3
-- We only handle 24 and 32 bit images.
| dib3BitCount headerV3 /= 24