HVIF parser almost minimally done
Still needs sections that don't appear in the sample file,
more stress-testing, and actual parsing of 24-bit floats.
Getty Ritter
8 years ago
3 | 3 | module Graphics.HVIF where |
4 | 4 | |
5 | 5 | import Control.Monad (replicateM, when) |
6 |
import Data.Bits ((.|.), |
|
6 | import Data.Bits ((.|.), (.&.), clearBit, shift, testBit) | |
7 | 7 | import Data.ByteString (ByteString) |
8 | 8 | import Data.Sequence (Seq) |
9 | 9 | import qualified Data.Sequence as S |
140 | 140 | |
141 | 141 | data Path = Path |
142 | 142 | { pathFlags :: PathFlags |
143 |
, pathPoints :: Seq |
|
143 | , pathPoints :: Seq Command | |
144 | 144 | } deriving (Eq, Show) |
145 | 145 | |
146 | 146 | pPath :: Get Path |
169 | 169 | , coordY :: Float |
170 | 170 | } deriving (Eq, Show) |
171 | 171 | |
172 | pPoints :: PathFlags -> Get (Seq Point) | |
173 | pPoints PathFlags { pfUsesCommands = False } = | |
174 | getSeveral (Point <$> pCoord <*> pCoord) | |
175 | pPoints PathFlags { pfUsesCommands = True } = do | |
176 | c1 <- getWord8 | |
177 | c2 <- getWord8 | |
178 | return $ S.empty | |
172 | data Command | |
173 | = CmdHLine Float | |
174 | | CmdVLine Float | |
175 | | CmdLine Point | |
176 | | CmdCurve Point Point Point | |
177 | deriving (Eq, Show) | |
178 | ||
179 | pPoints :: PathFlags -> Get (Seq Command) | |
180 | pPoints PathFlags { pfUsesCommands = False | |
181 | , pfNoCurves = False } = | |
182 | getSeveral pCurveCommand | |
183 | pPoints PathFlags { pfUsesCommands = False | |
184 | , pfNoCurves = True } = | |
185 | getSeveral pLineCommand | |
186 | pPoints PathFlags { pfUsesCommands = True } = | |
187 | pCommandList | |
188 | ||
189 | pLineCommand :: Get Command | |
190 | pLineCommand = CmdLine <$> (Point <$> pCoord <*> pCoord) | |
191 | ||
192 | pCurveCommand :: Get Command | |
193 | pCurveCommand = CmdCurve <$> (Point <$> pCoord <*> pCoord) | |
194 | <*> (Point <$> pCoord <*> pCoord) | |
195 | <*> (Point <$> pCoord <*> pCoord) | |
196 | ||
197 | pCommandList :: Get (Seq Command) | |
198 | pCommandList = do | |
199 | pointCount <- getWord8 | |
200 | let cmdByteCount = (pointCount + 3) `div` 4 | |
201 | cmdBytes <- replicateM (fromIntegral cmdByteCount) getWord8 | |
202 | let go n | |
203 | | n == fromIntegral pointCount = return S.empty | |
204 | | otherwise = | |
205 | let bIdx = n `div` 4 | |
206 | iIdx = (n `mod` 4) * 2 | |
207 | in case (cmdBytes !! bIdx) `shift` (negate iIdx) .&. 0x03 of | |
208 | 0x00 -> | |
209 | (S.<|) <$> (CmdHLine <$> pCoord) <*> go (n+1) | |
210 | 0x01 -> | |
211 | (S.<|) <$> (CmdVLine <$> pCoord) <*> go (n+1) | |
212 | 0x02 -> | |
213 | (S.<|) <$> pLineCommand <*> go (n+1) | |
214 | 0x03 -> | |
215 | (S.<|) <$> pCurveCommand <*> go (n+1) | |
216 | _ -> error "[unreachable]" | |
217 | go 0 | |
179 | 218 | |
180 | 219 | pCoord :: Get Float |
181 | 220 | pCoord = do |
182 | 221 | b1 <- getWord8 |
183 | 222 | if testBit b1 7 then do |
184 | 223 | b2 <- getWord8 |
185 | let cVal :: Word16 = (clearBit 7 (fromIntegral b1) `shift` 8) .|. fromIntegral b2 | |
186 | return ((fromIntegral cVal / 102.0) - 128.0) | |
224 | let cVal :: Word16 = (clearBit (fromIntegral b1) 7 `shift` 8) .|. fromIntegral b2 | |
225 | return (fromIntegral cVal / 102.0 - 128.0) | |
187 | 226 | else |
188 | 227 | return (fromIntegral b1 - 32.0) |
189 | 228 | |
193 | 232 | { shapeStyle :: StyleRef |
194 | 233 | , shapePaths :: Seq PathRef |
195 | 234 | , shapeFlags :: ShapeFlags |
196 |
|
|
235 | , shapeTransform :: Maybe Matrix | |
236 | } deriving (Eq, Show) | |
237 | ||
238 | type Matrix = Seq Float | |
197 | 239 | |
198 | 240 | pShape :: Get Shape |
199 | 241 | pShape = do |
242 | sType <- getWord8 | |
243 | when (sType /= 0x0a) $ | |
244 | fail ("Unknown shape type: " ++ show sType) | |
200 | 245 | shapeStyle <- StyleRef <$> get |
201 | 246 | shapePaths <- getSeveral (PathRef <$> get) |
202 | 247 | shapeFlags <- pShapeFlags |
248 | shapeTransform <- | |
249 | if sfTransform shapeFlags | |
250 | then Just <$> pMatrix | |
251 | else return Nothing | |
203 | 252 | return Shape { .. } |
204 | 253 | |
205 | 254 | newtype PathRef = PathRef { prIdx :: Word8 } deriving (Eq, Show) |
223 | 272 | , sfHasTransformers = testBit sFlags 4 |
224 | 273 | , sfTranslation = testBit sFlags 5 |
225 | 274 | } |
275 | ||
276 | pMatrix :: Get Matrix | |
277 | pMatrix = S.fromList `fmap` replicateM 6 pFloat | |
278 | ||
279 | pFloat :: Get Float | |
280 | pFloat = do | |
281 | _ <- getWord8 | |
282 | _ <- getWord8 | |
283 | _ <- getWord8 | |
284 | return 0.0 |