gruff-0.2: src/Tile.hs
{-# LANGUAGE CPP, ForeignFunctionInterface #-}
module Tile where
import Prelude hiding (log)
import Control.Exception (bracketOnError)
import Control.Monad (when)
import Data.Bits (shiftL, shiftR, (.&.))
import Foreign (Ptr, castPtr, sizeOf, mallocArray, free, Word8, with, poke, allocaBytes, withArray, peekArray)
import Foreign.C (CFloat, CDouble, CInt, CSize, withCStringLen)
import System.Directory (createDirectoryIfMissing)
import System.FilePath ((</>), (<.>))
import System.IO (withBinaryFile, IOMode(ReadMode,WriteMode), hPutBuf, hGetBuf)
--import Data.BEncode
import Numeric.QD (DoubleDouble, QuadDouble)
#ifdef HAVE_MPFR
import Foreign.C (CString, withCString)
import Number (R)
#endif
import QuadTree
import Utils (catchIO)
width, height, count, bytes :: Int
width = 256
height = 256
count = width * height
bytes = count * sizeOf (0 :: CFloat)
rootSquare :: Square
rootSquare = Square{ squareSize = 8, squareWest = -4, squareNorth = -4 }
data Tile = Tile Quad (Ptr CFloat) (Ptr CFloat) (Ptr CFloat)
mallocTile :: Quad -> IO Tile
mallocTile cs = do
ns <- mallocArray count
ds <- mallocArray count
ts <- mallocArray count
return $ Tile cs ns ds ts
freeTile :: Tile -> IO ()
freeTile (Tile _ ns ds ts) = do
free ns
free ds
free ts
header :: String
header = "RuFfTiLe001\n"
writeTile :: FilePath -> Tile -> IO ()
writeTile cacheDir (Tile q ns ds ts) = do
case filename q of
Nothing -> return ()
Just (dirs, file) -> do
let dir = foldr1 (</>) (cacheDir : dirs)
createDirectoryIfMissing True dir
withBinaryFile (dir </> file <.> "ruff") WriteMode $ \h -> do
withCStringLen header $ \(p, l) -> hPutBuf h p l
withArray (wordBE (bytes * 3)) $ \p -> hPutBuf h p 4
hPutBuf h ns bytes
hPutBuf h ds bytes
hPutBuf h ts bytes
-- hPut h . bPack . metaData $ q
where
wordBE :: Int -> [Word8]
wordBE n = map (\b -> fromIntegral $ (n `shiftR` (8 * b)) .&. 0xFF) [3, 2, 1, 0]
{-
metaData :: Quad -> BEncode
metaData q@Quad{ quadLevel = l, quadWest = w, quadNorth = n } =
BDict $ fromList [ ("tile", BDict $ fromList
[ ("about", BDict $ fromList
[ ("version", BInt 1)
, ("generator", BString $ pack "gruff-0.1") ]
, ("images", BDict . fromList . map image . zip [0..] $
[ "continuous dwell", "normalized distance", "final angle"]) ]
]
where
image plane alg = (alg, BDict $ fromList
[ ("width", BInt (fromIntegral width))
, ("height", BInt (fromIntegral height))
, ("real", BInt w)
, ("imag", BInt (negate n))
, ("scale", BInt (fromIntegral l + 2))
, ("format", BString $ pack "float32le")
, ("order", BString $ pack "lr,tb")
, ("data offset", BInt (fromIntegral $ plane * count * sizeOf (0 :: CFloat)))
])
-}
readTile :: FilePath -> Quad -> IO (Maybe Tile)
readTile cacheDir q = flip catchIO (\_ -> return Nothing) $ do
case filename q of
Nothing -> return Nothing
Just (dirs, file) -> do
let dir = foldr1 (</>) (cacheDir : dirs)
bracketOnError (mallocTile q) freeTile $ \t@(Tile _ ns ds ts) -> do
withBinaryFile (dir </> file <.> "ruff") ReadMode $ \h -> do
let headerBytes = 12
allocaBytes headerBytes $ \p -> do
headerBytes' <- hGetBuf h p headerBytes
when (headerBytes /= headerBytes') $ fail "readTile header fail"
header' <- peekArray headerBytes p
when (header' /= (map (fromIntegral . fromEnum) header :: [Word8])) $ fail "readTile header mismatch"
dataBytes <- allocaBytes 4 $ \p -> do
lenBytes' <- hGetBuf h p 4
when (lenBytes' /= 4) $ fail "readTile header length fail"
unwordBE `fmap` peekArray 4 p
when (dataBytes /= bytes * 3) $ fail "readTile header length mismatch"
bytes' <- hGetBuf h ns bytes
when (bytes /= bytes') $ fail ("readTile 0 " ++ show bytes ++ " /= " ++ show bytes')
bytes'' <- hGetBuf h ds bytes
when (bytes /= bytes'') $ fail ("readTile 1 " ++ show bytes ++ " /= " ++ show bytes'')
bytes''' <- hGetBuf h ts bytes
when (bytes /= bytes''') $ fail ("readTile 2 " ++ show bytes ++ " /= " ++ show bytes''')
return $ Just t
where
unwordBE :: [Word8] -> Int
unwordBE = sum . zipWith (\b n -> fromIntegral n `shiftL` (8 * b)) [3, 2, 1, 0]
clearTile :: Tile -> IO ()
clearTile (Tile _ ns ds ts) = do
mapM_ clear [ns, ds, ts]
where
clear p = c_memset (castPtr p) 0 (fromIntegral bytes)
computeTile :: (String -> IO ()) -> Ptr CInt -> Tile -> IO Bool
computeTile log p (Tile q@Quad{ quadLevel = l } ns ds ts) = do
its <- compute'
log $ show ("getTile", q, "computed", its)
return $ its /= 0
where
compute'
| l < 18 = c_compute_f32 p ns ds ts cx cy l' m'
| l < 48 = c_compute_f64 p ns ds ts cx cy l' m'
| l < 96 = with (cx :: DoubleDouble) $ \px -> with (cy :: DoubleDouble) $ \py ->
c_compute_f128 p ns ds ts (castPtr px) (castPtr py) l' m'
| l < 192 = with (cx :: QuadDouble ) $ \px -> with (cy :: QuadDouble ) $ \py ->
c_compute_f256 p ns ds ts (castPtr px) (castPtr py) l' m'
#ifdef HAVE_MPFR
| otherwise = withCString (show (cx :: R)) $ \px -> withCString (show (cy :: R)) $ \py ->
c_compute_mpfr p ns ds ts px py l' m'
#else
| otherwise = error "Tile.computeTile: too deep; recompile with MPFR"
#endif
l' = fromIntegral l
m' = 10000000
s = square rootSquare q
cx, cy :: Fractional a => a
cx = fromRational (squareWest s)
cy = fromRational (squareNorth s)
getTile :: (String -> IO ()) -> FilePath -> Ptr CInt -> Quad -> IO (Maybe Tile)
getTile log cacheDir p q = do
mTile <- readTile cacheDir q
case mTile of
Just t -> do
log $ show ("getTile", q, "read")
return (Just t)
Nothing -> do
log $ show ("getTile", q, "compute")
t <- mallocTile q
clearTile t
poke p 0
ok <- computeTile log p t
if ok
then writeTile cacheDir t >> return (Just t)
else freeTile t >> return Nothing
foreign import ccall unsafe "string.h memset"
c_memset :: Ptr Word8 -> CInt -> CSize -> IO (Ptr Word8)
foreign import ccall "compute.h compute_f32"
c_compute_f32 :: Ptr CInt -> Ptr CFloat -> Ptr CFloat -> Ptr CFloat -> CFloat -> CFloat -> CInt -> CInt -> IO CInt
foreign import ccall "compute.h compute_f64"
c_compute_f64 :: Ptr CInt -> Ptr CFloat -> Ptr CFloat -> Ptr CFloat -> CDouble -> CDouble -> CInt -> CInt -> IO CInt
foreign import ccall "compute.h compute_f128"
c_compute_f128 :: Ptr CInt -> Ptr CFloat -> Ptr CFloat -> Ptr CFloat -> Ptr CDouble -> Ptr CDouble -> CInt -> CInt -> IO CInt
foreign import ccall "compute.h compute_f256"
c_compute_f256 :: Ptr CInt -> Ptr CFloat -> Ptr CFloat -> Ptr CFloat -> Ptr CDouble -> Ptr CDouble -> CInt -> CInt -> IO CInt
#ifdef HAVE_MPFR
foreign import ccall "compute.h compute_mpfr"
c_compute_mpfr :: Ptr CInt -> Ptr CFloat -> Ptr CFloat -> Ptr CFloat -> CString -> CString -> CInt -> CInt -> IO CInt
#endif