animascii-0.1.0.0: src/Export/Gif.hs
{-# Language NoMonomorphismRestriction #-}
module Export.Gif where
import Parse
import Config
import Terminal.Game
import Control.Exception
import System.Process
import System.FilePath
import System.Exit
import System.IO.Temp
import qualified System.Process.Internals as SPI
import qualified Data.Foldable as F
writeGif :: Integer -> FilePath -> (Colour, Colour) ->
Animation -> IO (Maybe ExpExc)
writeGif fps fp clp fs = catcher
where
gifProcess :: FilePath -> [String] -> CreateProcess
gifProcess d fas = shell . unwords $
["convert -loop 0"] ++
fas ++
[fp]
makeGif d = mapM (renderFrame d fps clp) (getFrames fs) >>= \fas ->
execProcess (gifProcess d fas)
catcher :: IO (Maybe ExpExc)
catcher = catch (withSystemTempDirectory "animascii" makeGif >>
return Nothing)
(return . Just)
------------
-- RENDER --
------------
-- will create an image in temporary folder and return the appropriate
-- argument for imagemagick's "convert" (e.g. -delay 300 10.png)
renderFrame :: FilePath -> Integer -> (Colour, Colour) ->
Frame Plane Integer -> IO String
renderFrame d fps (fgc, bgc) (i, p) =
writeTempFile d "frame.png" "" >>= \tf ->
let dtf = d </> tf in
execProcess (frameProcess dtf) >>
return (toReturn dtf)
where
frameProcess :: FilePath -> CreateProcess
frameProcess f = shell . unwords $
["convert",
"-background " ++ bgc,
"-fill " ++ fgc,
"-font Courier",
"-pointsize 18",
"label:" ++ pp,
f]
pp = let x = init $ paperPlane p
y = case x of
(' ':xs) -> '\\' : imEscape x -- needed by imagemagick
as -> imEscape as
in SPI.translate y
toReturn n = unwords ["-delay " ++ show i',
n]
i' = round $ (fromIntegral i) * 100 / fromIntegral fps
-----------------
-- ANCILLARIES --
-----------------
-- exception type
type ErrMess = String
data ExpExc = ExpExc ExitCode ErrMess
deriving (Show, Eq)
instance Exception ExpExc
makeExc :: ExitCode -> ErrMess -> ExpExc
makeExc ec em = ExpExc ec em
-- catch/throw, since we're using an external program
execProcess :: CreateProcess -> IO ()
execProcess cp = readCreateProcessWithExitCode cp "" >>= \(ec, so, se) ->
if ec == ExitSuccess
then return ()
else throwIO (makeExc ec se)
-- see https://github.com/haskell/process/issues/120
imEscape :: String -> String
imEscape cs = F.foldMap f cs
where
f '\\' = "\\\\"
f c = [c]