{-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Codec.BMP.Unpack
	(unpackBMPToRGBA32)
where	
import Codec.BMP.Base
import Codec.BMP.BitmapInfo
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.
--	The alpha component is set to 255 for every pixel.
unpackBMPToRGBA32 :: BMP -> ByteString
unpackBMPToRGBA32 bmp 
 = case bmpBitmapInfo bmp of
	InfoV3 info
	 -> packRGB24ToRGBA32 
			(fromIntegral $ dib3Width info) 
			(fromIntegral $ dib3Height info)
			(bmpRawImageData bmp)


-- | 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
   in	if height * (width * 3 + padPerLine) /= BS.length str
	 then error "Codec.BMP.unpackRGB24ToRGBA32: given image dimensions don't match input data."
 	 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)