{-# OPTIONS -fwarn-incomplete-patterns #-}

-- | Helper functions for rendering bitmaps
module Graphics.Gloss.Internals.Render.Bitmap
	( BitmapData(..)
	, reverseRGBA
	, bitmapPath
	, freeBitmapData
	)
where
import Data.Data
import Foreign


-- | Abstract 32-bit RGBA bitmap data.
data BitmapData 
        = BitmapData 
                Int                     -- length (in bytes)
                (ForeignPtr Word8)      -- pointer to data
        deriving (Eq, Data, Typeable)


instance Show BitmapData where
 show _ = "BitmapData"


-- | Generates the point path to display the bitmap centred
bitmapPath :: Float -> Float -> [(Float, Float)]
bitmapPath width height 
 = [(-width', -height'), (width', -height'), (width', height'), (-width', height')]
 where	width'  = width  / 2
	height' = height / 2


-- | Destructively reverse the byte order in an array.
--   This is necessary as OpenGL reads pixel data as ABGR, rather than RGBA
reverseRGBA :: BitmapData -> IO ()
reverseRGBA (BitmapData length8 fptr)
 = withForeignPtr fptr (reverseRGBA_ptr length8)


-- | Destructively reverses the byte order in an array.
reverseRGBA_ptr :: Int -> Ptr Word8 -> IO ()
reverseRGBA_ptr length8 ptr8
 = go (length8 `div` 4) (castPtr ptr8) 0
 where
        go :: Int -> Ptr Word32 -> Int -> IO ()
        go len ptr count
         | count < len 
         = do	curr <- peekElemOff ptr count
      	        let byte0 = shift (isolateByte0 curr) 24
      	        let byte1 = shift (isolateByte1 curr) 8
      	        let byte2 = shift (isolateByte2 curr) (-8)
      	        let byte3 = shift (isolateByte3 curr) (-24)
      	        pokeElemOff ptr count (byte0 .|. byte1 .|. byte2 .|. byte3)
      	        go len ptr (count + 1)

         | otherwise 
         = return ()

-- | Frees the allocated memory given to OpenGL to avoid a memory leak
freeBitmapData :: Ptr Word8 -> IO ()
{-# INLINE freeBitmapData #-}
freeBitmapData p = free p


-- | These functions work as bit masks to isolate the Word8 components
{-# INLINE isolateByte0 #-}
isolateByte0 :: Word32 -> Word32
isolateByte0 word =
   word .&. (255 :: Word32)

{-# INLINE isolateByte1 #-}
isolateByte1 :: Word32 -> Word32
isolateByte1 word =
   word .&. (65280 :: Word32)

{-# INLINE isolateByte2 #-}
isolateByte2 :: Word32 -> Word32
isolateByte2 word =
   word .&. (16711680 :: Word32)

{-# INLINE isolateByte3 #-}
isolateByte3 :: Word32 -> Word32
isolateByte3 word =
   word .&. (4278190080 :: Word32)