module Codec.Image.DevIL
( ilInit
, readImage, writeImage, writeImageFromPtr
, Word8
)
where
import Data.Int
import Data.Word
import Foreign hiding (newArray)
import Foreign.C
import Data.Array.Storable
import Data.Array.Unboxed
import Control.Applicative
import Control.Monad
import System.IO.Unsafe (unsafeInterleaveIO)
#include "IL/il.h"
type ILuint = #type ILuint
type ILsizei = #type ILsizei
type ILboolean = #type ILboolean
type ILenum = #type ILenum
type ILint = #type ILint
type ILubyte = #type ILubyte
newtype ImageName = ImageName { fromImageName :: ILuint }
-- | Initialize the library.
foreign import CALLTYPE "ilInit" ilInitC :: IO ()
foreign import CALLTYPE "ilOriginFunc" ilOriginFuncC :: ILenum -> IO ILboolean
foreign import CALLTYPE "ilEnable" ilEnableC :: ILenum -> IO ILboolean
ilInit :: IO ()
ilInit = do
ilInitC
ilOriginFuncC (#const IL_ORIGIN_LOWER_LEFT)
ilEnableC (#const IL_ORIGIN_SET)
return ()
-- | Reads an image into an RGBA array. Indices are (row,column,color-channel).
readImage :: FilePath -> IO (UArray (Int,Int,Int) Word8)
readImage x =
do [inname] <- ilGenImages 1
ilBindImage inname
ilLoadImage x
a <- toArrayRGBA
ilDeleteImages [inname]
return a
-- | Writes an RGBA array to a file. Indices are (row,column,color-channel).
writeImage :: FilePath -> UArray (Int,Int,Int) Word8 -> IO ()
writeImage f a =
do [outname] <- ilGenImages 1
ilBindImage outname
fromArrayRGBA a
ilSaveImage f
ilDeleteImages [outname]
foreign import CALLTYPE "ilGenImages" ilGenImagesC
:: ILsizei -> Ptr ILuint -> IO ()
ilGenImages :: Int -> IO [ImageName]
ilGenImages num = do
ar <- newArray (0, num-1) 0
withStorableArray ar $ \p -> do
ilGenImagesC (fromIntegral num) p
map ImageName <$> getElems ar
foreign import CALLTYPE "ilBindImage" ilBindImageC :: ILuint -> IO ()
ilBindImage :: ImageName -> IO ()
ilBindImage (ImageName name) = ilBindImageC name
foreign import CALLTYPE "ilDeleteImages" ilDeleteImagesC
:: ILsizei -> Ptr ILuint -> IO ()
ilDeleteImages :: [ImageName] -> IO ()
ilDeleteImages names = do
ar <- newListArray (0, length names-1) (fromImageName <$> names)
withStorableArray ar $ \p -> do
ilDeleteImagesC (fromIntegral $ length names) p
foreign import CALLTYPE "ilLoadImage" ilLoadImageC :: CString -> IO ILboolean
ilLoadImage :: FilePath -> IO Bool
ilLoadImage file = do
(0 /=) <$> withCString file ilLoadImageC
foreign import CALLTYPE "ilSaveImage" ilSaveImageC :: CString -> IO ILboolean
ilSaveImage :: FilePath -> IO Bool
ilSaveImage file = do
(0 /=) <$> withCString file ilSaveImageC
foreign import CALLTYPE "ilConvertImage" ilConvertImageC
:: ILenum -> ILenum -> IO Bool
foreign import CALLTYPE "ilGetInteger" ilGetIntegerC
:: ILenum -> IO ILint
foreign import CALLTYPE "ilCopyPixels" ilCopyPixelsC
:: ILuint -> ILuint -> ILuint -- x y z
-> ILuint -> ILuint -> ILuint -- w h depth
-> ILenum -> ILenum -- format type
-> Ptr () -- data (copy into this pointer)
-> IO ()
foreign import CALLTYPE "ilSetPixels" ilSetPixelsC
:: ILuint -> ILuint -> ILuint -- x y z
-> ILuint -> ILuint -> ILuint -- w h depth
-> ILenum -> ILenum -- format type
-> Ptr () -- data (copy from this pointer)
-> IO ()
foreign import CALLTYPE "ilTexImage" ilTexImageC
:: ILuint -> ILuint -> ILuint -- w h depth
-> ILubyte -> ILenum -> ILenum -- numberOfChannels format type
-> Ptr () -- data (copy from this pointer)
-> IO Bool
il_RGBA = (#const IL_RGBA) :: ILenum
il_UNSIGNED_BYTE = (#const IL_UNSIGNED_BYTE) :: ILenum
il_IMAGE_HEIGHT = (#const IL_IMAGE_HEIGHT) :: ILenum
il_IMAGE_WIDTH = (#const IL_IMAGE_WIDTH) :: ILenum
-- array indices are (x,y,channel) where channel: 0=Red, 1=Green, 2=Blue, 3=Alpha
toArrayRGBA :: IO (UArray (Int,Int,Int) Word8)
toArrayRGBA = do
-- Arrays are stored in row major, so we have to be fiddly with
-- our use of terminology.
columns <- ilGetIntegerC il_IMAGE_WIDTH
rows <- ilGetIntegerC il_IMAGE_HEIGHT
let bounds = ((0,0,0), (fromIntegral rows-1, fromIntegral columns-1, 3))
ar <- newArray_ bounds
withStorableArray ar $ \p -> do
ilCopyPixelsC 0 0 0
(fromIntegral columns) (fromIntegral rows) 1
il_RGBA il_UNSIGNED_BYTE
(castPtr p)
listArray bounds <$> lazyElems ar
lazyElems ar = do
ixs <- range <$> getBounds ar
go ixs
where
go [] = return []
go (ix:ixs) = unsafeInterleaveIO $ liftM2 (:) (readArray ar ix) (go ixs)
-- same as toArrayRGBA
fromArrayRGBA :: UArray (Int,Int,Int) Word8 -> IO ()
fromArrayRGBA dat = do
let ((0,0,0), (maxrow,maxcol,3)) = bounds dat
ar <- unsafeThaw dat
withStorableArray ar $ \p -> do
ilTexImageC (fromIntegral maxcol+1) (fromIntegral maxrow+1) 1
4 il_RGBA il_UNSIGNED_BYTE
(castPtr p)
return ()
-- | Write an image from a pointer to raw RGBA data. Careful!
-- The size tuple is (rows, columns), not (width, height).
writeImageFromPtr :: FilePath -> (Int,Int) -> Ptr Word8 -> IO ()
writeImageFromPtr f (rows,cols) p = do
[outname] <- ilGenImages 1
ilBindImage outname
fromPtrRGBA (rows,cols) p
ilSaveImage f
ilDeleteImages [outname]
fromPtrRGBA :: (Int,Int) -> Ptr Word8 -> IO ()
fromPtrRGBA (rows,cols) p = do
ilTexImageC (fromIntegral cols) (fromIntegral rows) 1
4 il_RGBA il_UNSIGNED_BYTE
(castPtr p)
return ()
-- vim: ft=haskell :