bmp-1.2.0.1: Codec/BMP/Unpack.hs
{-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Codec.BMP.Unpack
(unpackBMPToRGBA32)
where
import Codec.BMP.Base
import Codec.BMP.BitmapInfo
import Codec.BMP.BitmapInfoV3
import Foreign.Ptr
import Foreign.Marshal.Alloc
import Foreign.Storable
import System.IO.Unsafe
import Data.Word
import Data.ByteString as BS
import Data.ByteString.Unsafe as BS
import Prelude as P
-- | Unpack a BMP image to a string of RGBA component values.
unpackBMPToRGBA32 :: BMP -> ByteString
unpackBMPToRGBA32 bmp
= let info = getBitmapInfoV3 $ bmpBitmapInfo bmp
width = fromIntegral $ dib3Width info
height = fromIntegral $ dib3Height info
bitCount = dib3BitCount info
in case bitCount of
24 -> packRGB24ToRGBA32 width height (bmpRawImageData bmp)
32 -> packRGB32ToRGBA32 width height (bmpRawImageData bmp)
_ -> error "Codec.BMP.unpackBMPToRGBA32: unhandled bitcount."
-- | Unpack raw, uncompressed 24 bit BMP image data to a string of RGBA component values.
-- The alpha component is set to 255 for every pixel.
packRGB24ToRGBA32
:: Int -- Width of image.
-> Int -- Height of image.
-> ByteString -- Input string.
-> ByteString
packRGB24ToRGBA32 width height str
= let bytesPerLine = BS.length str `div` height
padPerLine = bytesPerLine - width * 3
sizeDest = width * height * 4
-- We allow padding bytes on the end of the image data.
in if BS.length str < height * (width * 3 + padPerLine)
then error "Codec.BMP.unpackRGB24ToRGBA32: image data is truncated."
else unsafePerformIO
$ allocaBytes sizeDest $ \bufDest ->
BS.unsafeUseAsCString str $ \bufSrc ->
do packRGB24ToRGBA32' width height padPerLine (castPtr bufSrc) (castPtr bufDest)
packCStringLen (bufDest, sizeDest)
-- We're doing this via Ptrs because we don't want to take the
-- overhead of doing the bounds checks in ByteString.index.
packRGB24ToRGBA32' width height pad ptrSrc ptrDest
= go 0 0 0 0
where
go posX posY oSrc oDest
-- skip over padding bytes at the end of each line.
| posX == width
= go 0 (posY + 1) (oSrc + pad) oDest
-- we've finished the image.
| posY == height
= return ()
-- process a pixel.
| otherwise
= do blue :: Word8 <- peekByteOff ptrSrc (oSrc + 0)
green :: Word8 <- peekByteOff ptrSrc (oSrc + 1)
red :: Word8 <- peekByteOff ptrSrc (oSrc + 2)
pokeByteOff ptrDest (oDest + 0) red
pokeByteOff ptrDest (oDest + 1) green
pokeByteOff ptrDest (oDest + 2) blue
pokeByteOff ptrDest (oDest + 3) (255 :: Word8)
go (posX + 1) posY (oSrc + 3) (oDest + 4)
-- | Unpack raw, uncompressed 32 bit BMP image data to a string of RGBA component values.
-- Note in the BMP file the components are arse-around ABGR instead of RGBA.
-- The 'unpacking' here is really just flipping the components around.
packRGB32ToRGBA32
:: Int -- Width of image.
-> Int -- Height of image.
-> ByteString -- Input string.
-> ByteString
packRGB32ToRGBA32 width height str
= let sizeDest = height * width * 4
in if BS.length str < sizeDest
then error "Codec.BMP.packRGB24ToRGBA32: image data is truncated."
else unsafePerformIO
$ allocaBytes sizeDest $ \bufDest ->
BS.unsafeUseAsCString str $ \bufSrc ->
do packRGB32ToRGBA32' width height (castPtr bufSrc) (castPtr bufDest)
packCStringLen (bufDest, sizeDest)
-- We're doing this via Ptrs because we don't want to take the
-- overhead of doing the bounds checks in ByteString.index.
packRGB32ToRGBA32' width height ptrSrc ptrDest
= go 0 0 0 0
where
go posX posY oSrc oDest
-- skip over padding bytes at the end of each line.
| posX == width
= go 0 (posY + 1) oSrc oDest
-- we've finished the image.
| posY == height
= return ()
-- process a pixel.
| otherwise
= do blue :: Word8 <- peekByteOff ptrSrc (oSrc + 0)
green :: Word8 <- peekByteOff ptrSrc (oSrc + 1)
red :: Word8 <- peekByteOff ptrSrc (oSrc + 2)
alpha :: Word8 <- peekByteOff ptrSrc (oSrc + 3)
pokeByteOff ptrDest (oDest + 0) red
pokeByteOff ptrDest (oDest + 1) green
pokeByteOff ptrDest (oDest + 2) blue
pokeByteOff ptrDest (oDest + 3) alpha
go (posX + 1) posY (oSrc + 4) (oDest + 4)