never executed always true always false
1 {-# LANGUAGE MultiWayIf #-}
2 {-# LANGUAGE RecordWildCards #-}
3 module Reanimate.Driver
4 ( reanimate
5 )
6 where
7
8 import Control.Applicative ((<|>))
9 import Control.Monad
10 import Data.Maybe
11 import Data.Either
12 import Reanimate.Animation (Animation)
13 import Reanimate.Driver.Check
14 import Reanimate.Driver.CLI
15 import Reanimate.Driver.Compile
16 import Reanimate.Driver.Server
17 import Reanimate.Parameters
18 import Reanimate.Render (render, renderSnippets, renderSvgs,
19 selectRaster)
20 import System.Directory
21 import System.Exit
22 import System.FilePath
23 import System.IO
24 import Text.Printf
25
26 presetFormat :: Preset -> Format
27 presetFormat Youtube = RenderMp4
28 presetFormat ExampleGif = RenderGif
29 presetFormat Quick = RenderMp4
30 presetFormat MediumQ = RenderMp4
31 presetFormat HighQ = RenderMp4
32 presetFormat LowFPS = RenderMp4
33
34 presetFPS :: Preset -> FPS
35 presetFPS Youtube = 60
36 presetFPS ExampleGif = 25
37 presetFPS Quick = 15
38 presetFPS MediumQ = 30
39 presetFPS HighQ = 30
40 presetFPS LowFPS = 10
41
42 presetWidth :: Preset -> Width
43 presetWidth Youtube = 2560
44 presetWidth ExampleGif = 320
45 presetWidth Quick = 320
46 presetWidth MediumQ = 800
47 presetWidth HighQ = 1920
48 presetWidth LowFPS = presetWidth HighQ
49
50 presetHeight :: Preset -> Height
51 presetHeight preset = presetWidth preset * 9 `div` 16
52
53 formatFPS :: Format -> FPS
54 formatFPS RenderMp4 = 60
55 formatFPS RenderGif = 25
56 formatFPS RenderWebm = 60
57
58 formatWidth :: Format -> Width
59 formatWidth RenderMp4 = 2560
60 formatWidth RenderGif = 320
61 formatWidth RenderWebm = 2560
62
63 formatHeight :: Format -> Height
64 formatHeight RenderMp4 = 1440
65 formatHeight RenderGif = 180
66 formatHeight RenderWebm = 1440
67
68 formatExtension :: Format -> String
69 formatExtension RenderMp4 = "mp4"
70 formatExtension RenderGif = "gif"
71 formatExtension RenderWebm = "webm"
72
73 {-|
74 Main entry-point for accessing an animation. Creates a program that takes the
75 following command-line arguments:
76
77 > Usage: PROG [COMMAND]
78 > This program contains an animation which can either be viewed in a web-browser
79 > or rendered to disk.
80 >
81 > Available options:
82 > -h,--help Show this help text
83 >
84 > Available commands:
85 > check Run a system's diagnostic and report any missing
86 > external dependencies.
87 > view Play animation in browser window.
88 > render Render animation to file.
89
90 Neither the \'check\' nor the \'view\' command take any additional arguments.
91 Rendering animation can be controlled with these arguments:
92
93 > Usage: PROG render [-o|--target FILE] [--fps FPS] [-w|--width PIXELS]
94 > [-h|--height PIXELS] [--compile] [--format FMT]
95 > [--preset TYPE]
96 > Render animation to file.
97 >
98 > Available options:
99 > -o,--target FILE Write output to FILE
100 > --fps FPS Set frames per second.
101 > -w,--width PIXELS Set video width.
102 > -h,--height PIXELS Set video height.
103 > --compile Compile source code before rendering.
104 > --format FMT Video format: mp4, gif, webm
105 > --preset TYPE Parameter presets: youtube, gif, quick
106 > -h,--help Show this help text
107 -}
108 reanimate :: Animation -> IO ()
109 reanimate animation = do
110 Options {..} <- getDriverOptions
111 case optsCommand of
112 Raw {..} -> do
113 setFPS 60
114 renderSvgs rawOutputFolder rawFrameOffset rawPrettyPrint animation
115 Test -> do
116 setNoExternals True
117 -- hSetBinaryMode stdout True
118 renderSnippets animation
119 Check -> checkEnvironment
120 View {..} -> serve viewVerbose viewGHCPath viewGHCOpts viewOrigin
121 Render {..} -> do
122 let fmt =
123 guessParameter renderFormat (fmap presetFormat renderPreset)
124 $ case renderTarget of
125 -- Format guessed from output
126 Just target -> case takeExtension target of
127 ".mp4" -> RenderMp4
128 ".gif" -> RenderGif
129 ".webm" -> RenderWebm
130 _ -> RenderMp4
131 -- Default to mp4 rendering.
132 Nothing -> RenderMp4
133
134 target <- case renderTarget of
135 Nothing -> do
136 mbSelf <- findOwnSource
137 let ext = formatExtension fmt
138 self = fromMaybe "output" mbSelf
139 pure $ replaceExtension self ext
140 Just target -> makeAbsolute target
141
142 let
143 fps =
144 guessParameter renderFPS (fmap presetFPS renderPreset) $ formatFPS fmt
145 (width, height) = fromMaybe
146 ( maybe (formatWidth fmt) presetWidth renderPreset
147 , maybe (formatHeight fmt) presetHeight renderPreset
148 )
149 (userPreferredDimensions renderWidth renderHeight)
150
151 raster <-
152 if renderRaster == RasterNone || renderRaster == RasterAuto then do
153 svgSupport <- hasFFmpegRSvg
154 if isRight svgSupport
155 then selectRaster renderRaster
156 else do
157 raster <- selectRaster RasterAuto
158 when (raster == RasterNone) $ do
159 hPutStrLn stderr
160 "Error: your FFmpeg was built without SVG support and no raster engines \
161 \are available. Please install either inkscape, imagemagick, or rsvg."
162 exitWith (ExitFailure 1)
163 return raster
164 else selectRaster renderRaster
165
166 if renderCompile
167 then compile $
168 [ "render"
169 , "--fps"
170 , show fps
171 , "--width"
172 , show width
173 , "--height"
174 , show height
175 , "--format"
176 , showFormat fmt
177 , "--raster"
178 , showRaster raster
179 , "--target"
180 , target
181 , "+RTS"
182 , "-N"
183 , "-RTS"
184 ] ++ [ "--partial" | renderPartial ]
185 else do
186 setRaster raster
187 setFPS fps
188 setWidth width
189 setHeight height
190 printf
191 "Animation options:\n\
192 \ fps: %d\n\
193 \ width: %d\n\
194 \ height: %d\n\
195 \ fmt: %s\n\
196 \ target: %s\n\
197 \ raster: %s\n"
198 fps
199 width
200 height
201 (showFormat fmt)
202 target
203 (show raster)
204
205 render animation target raster fmt width height fps renderPartial
206
207 guessParameter :: Maybe a -> Maybe a -> a -> a
208 guessParameter a b def = fromMaybe def (a <|> b)
209
210
211 -- If user specifies exactly one dimension explicitly, calculate the other
212 userPreferredDimensions :: Maybe Width -> Maybe Height -> Maybe (Width, Height)
213 userPreferredDimensions (Just width) (Just height) = Just (width, height)
214 userPreferredDimensions (Just width) Nothing =
215 Just (width, makeEven $ width * 9 `div` 16)
216 userPreferredDimensions Nothing (Just height) =
217 Just (makeEven $ height * 16 `div` 9, height)
218 userPreferredDimensions Nothing Nothing = Nothing
219
220 -- Avoid ffmpeg failures "height not divisible by 2"
221 makeEven :: Int -> Int
222 makeEven x | even x = x
223 | otherwise = x - 1