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
9 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 | |