module Graphics.Text.TrueType.Bytecode where
import Prelude hiding ( EQ, GT, LT )
import Control.Applicative( (<$>) )
import Data.Bits( (.&.), testBit )
import Data.Binary.Get( Get, getWord8, getWord16be )
import Data.Monoid( mempty )
import Data.Int( Int32 )
import qualified Data.Map as M
import Data.Word( Word8, Word16 )
import qualified Data.Vector.Unboxed as VU
import Graphics.Text.TrueType.Types
type InstructionFlag = Bool
data Instruction
= AA
| ABS
| ADD
| ALIGNPTS
| ALIGNRP
| AND
| CALL
| CEILING
| CINDEX
| CLEAR
| DEBUG
| DELTAC1
| DELTAC2
| DELTAC3
| DELTAP1
| DELTAP2
| DELTAP3
| DEPTH
| DIV
| DUP
| EIF
| ELSE
| ENDF
| EQ
| EVEN
| FDEF
| FLIPOFF
| FLIPON
| FLIPPT
| FLIPRGOFF
| FLIPRGON
| FLOOR
| GC PositionSource
| GETINFO
| GFV
| GPV
| GT
| GTEQ
| IDEF
| IF
| INSTCTRL
| IP
| ISECT
| IUP Direction
| JMPR
| JROF
| JROT
| LOOPCALL
| LT
| LTEQ
| MAX
| MD PositionSource
| MDAP Rounding
| MDRP PointReset DistanceKeeping Rounding Word8
| MIAP Rounding
| MIN
| MINDEX
| MIRP PointReset DistanceKeeping Rounding Word8
| MPPEM
| MPS
| MSIRP PointReset
| MUL
| NEG
| NEQ
| NOT
| NPUSHB (VU.Vector Word8)
| NPUSHW (VU.Vector Word16)
| NROUND Word8
| ODD
| OR
| POP
| PUSHB (VU.Vector Word8)
| PUSHW (VU.Vector Word16)
| RCVT
| RDTG
| ROFF
| ROLL
| ROUND Word8
| RS
| RTDG
| RTG
| RTHG
| RUTG
| S45ROUND
| SANGW
| SCANCTRL
| SCANTYPE
| SCFS
| SCVTCI
| SDB
| SDPVTL Parallelism
| SDS
| SFVFS
| SFVTCA Direction
| SFVTL Parallelism
| SFVTPV
| SHC CountourRef
| SHP CountourRef
| SHPIX
| SHZ CountourRef
| SLOOP
| SMD
| SPVFS
| SPVTCA Direction
| SPVTL Parallelism
| SROUND
| SRP0
| SRP1
| SRP2
| SSW
| SSWCI
| SUB
| SVTCA Direction
| SWAP
| SZP0
| SZP1
| SZP2
| SZPS
| UTP
| WCVTF
| WCVTP
| WS
deriving (Eq, Show)
data CountourRef
= UseRP2InZP1
| UseRP1InZP0
deriving (Eq, Show)
data Parallelism
= VectorParallel
| VectorPerpendicular
deriving (Eq, Show)
data PositionSource
= PositionCurrent
| PositionOriginal
deriving (Eq, Show)
data Direction
= DirectionX
| DirectionY
deriving (Eq, Show)
data Rounding
= RoundingNone
| Rounding
deriving (Eq, Show)
data PointReset
= ResetNot
| ResetRP0
deriving (Eq, Show)
data DistanceKeeping
= DistanceFree
| DistanceGreaterThanMin
deriving (Eq, Show)
data ByteCodeProgram
= If [ByteCodeProgram]
| IfElse [ByteCodeProgram] [ByteCodeProgram]
| Function [ByteCodeProgram]
| Sequence [Instruction]
deriving (Eq, Show)
instructionToProgram :: [Instruction]-> [ByteCodeProgram]
instructionToProgram = firstOfThird . go []
where
firstOfThird (f, _, _) = f
go acc [] = (reverse acc, [], [])
go acc (IF : rest) =
case go [] rest of
(ifBranch, [], final) -> go (If ifBranch : acc) final
(ifBranch, elseBranch, final) ->
go (IfElse ifBranch elseBranch : acc) final
go acc (EIF : rest) = (reverse acc, [], rest)
go acc (ELSE : rest) = (acc, elseBranch, final)
where (elseBranch, _, final) = go [] rest
go acc (FDEF : rest) = go (Function def: acc) final
where (def, _, final) = go [] rest
go acc (ENDF : rest) = (reverse acc, [], rest)
go acc (x : xs) = go (Sequence [x] : acc) xs
inRange :: Ord a => a -> (a, a) -> Bool
inRange v (lo, hi) = lo <= v && v <= hi
getInstr :: Get Instruction
getInstr = getWord8 >>= go
where
mirpParse x constructor = constructor reset distance rounding distanceType
where
reset | x `testBit` 4 = ResetRP0
| otherwise = ResetNot
distance | x `testBit` 3 = DistanceGreaterThanMin
| otherwise = DistanceFree
rounding | x `testBit` 2 = Rounding
| otherwise = RoundingNone
distanceType = x .&. 0x3
go w = case w of
0x7F -> return AA
0x64 -> return ABS
0x60 -> return ADD
0x27 -> return ALIGNPTS
0x3C -> return ALIGNRP
0x5A -> return AND
0x2B -> return CALL
0x67 -> return CEILING
0x25 -> return CINDEX
0x22 -> return CLEAR
0x4F -> return DEBUG
0x73 -> return DELTAC1
0x74 -> return DELTAC2
0x75 -> return DELTAC3
0x5D -> return DELTAP1
0x71 -> return DELTAP2
0x72 -> return DELTAP3
0x24 -> return DEPTH
0x62 -> return DIV
0x20 -> return DUP
0x59 -> return EIF
0x1B -> return ELSE
0x2D -> return ENDF
0x54 -> return EQ
0x57 -> return EVEN
0x2C -> return FDEF
0x4E -> return FLIPOFF
0x4D -> return FLIPON
0x80 -> return FLIPPT
0x82 -> return FLIPRGOFF
0x81 -> return FLIPRGON
0x66 -> return FLOOR
0x46 -> return $ GC PositionCurrent
0x47 -> return $ GC PositionOriginal
0x88 -> return GETINFO
0x0D -> return GFV
0x0C -> return GPV
0x52 -> return GT
0x53 -> return GTEQ
0x89 -> return IDEF
0x58 -> return IF
0x8E -> return INSTCTRL
0x39 -> return IP
0x0F -> return ISECT
0x30 -> return $ IUP DirectionY
0x31 -> return $ IUP DirectionX
0x1C -> return JMPR
0x79 -> return JROF
0x78 -> return JROT
0x2A -> return LOOPCALL
0x50 -> return LT
0x51 -> return LTEQ
0x8B -> return MAX
0x49 -> return $ MD PositionCurrent
0x4A -> return $ MD PositionOriginal
0x2E -> return $ MDAP RoundingNone
0x2F -> return $ MDAP Rounding
x | x `inRange` (0xC0, 0xDF) -> return $ mirpParse x MDRP
0x3E -> return $ MIAP RoundingNone
0x3F -> return $ MIAP Rounding
0x8C -> return MIN
0x26 -> return MINDEX
x | x `inRange` (0xE0, 0xFF) -> return $ mirpParse x MIRP
0x4B -> return MPPEM
0x4C -> return MPS
0x3A -> return $ MSIRP ResetNot
0x3B -> return $ MSIRP ResetRP0
0x63 -> return MUL
0x65 -> return NEG
0x55 -> return NEQ
0x5C -> return NOT
0x40 -> do
count <- fromIntegral <$> getWord8
NPUSHB <$> VU.replicateM count getWord8
0x41 -> do
count <- fromIntegral <$> getWord8
NPUSHW <$> VU.replicateM count getWord16be
x | x `inRange` (0x6C, 0x6F) ->
return . NROUND $ x .&. 3
0x56 -> return ODD
0x5B -> return OR
0x21 -> return POP
x | x `inRange` (0xB0, 0xB7) -> do
let count = fromIntegral $ (x .&. 0x7) + 1
PUSHB <$> VU.replicateM count getWord8
x | x `inRange` (0xB8, 0xBF) -> do
let count = fromIntegral $ (x .&. 0x7) + 1
PUSHW <$> VU.replicateM count getWord16be
0x45 -> return RCVT
0x7D -> return RDTG
0x7A -> return ROFF
0x8A -> return ROLL
x | x `inRange` (0x68, 0x6B) ->
return $ ROUND (x .&. 0x3)
0x43 -> return RS
0x3D -> return RTDG
0x18 -> return RTG
0x19 -> return RTHG
0x7C -> return RUTG
0x77 -> return S45ROUND
0x7E -> return SANGW
0x85 -> return SCANCTRL
0x8D -> return SCANTYPE
0x48 -> return SCFS
0x1D -> return SCVTCI
0x5E -> return SDB
0x86 -> return $ SDPVTL VectorParallel
0x87 -> return $ SDPVTL VectorPerpendicular
0x5F -> return SDS
0x0B -> return SFVFS
0x04 -> return $ SFVTCA DirectionY
0x05 -> return $ SFVTCA DirectionX
0x08 -> return $ SFVTL VectorParallel
0x09 -> return $ SFVTL VectorPerpendicular
0x0E -> return SFVTPV
0x34 -> return $ SHC UseRP2InZP1
0x35 -> return $ SHC UseRP1InZP0
0x32 -> return $ SHP UseRP2InZP1
0x33 -> return $ SHP UseRP1InZP0
0x38 -> return SHPIX
0x36 -> return $ SHZ UseRP2InZP1
0x37 -> return $ SHZ UseRP1InZP0
0x17 -> return SLOOP
0x1A -> return SMD
0x0A -> return SPVFS
0x02 -> return $ SPVTCA DirectionY
0x03 -> return $ SPVTCA DirectionX
0x06 -> return $ SPVTL VectorParallel
0x07 -> return $ SPVTL VectorPerpendicular
0x76 -> return SROUND
0x10 -> return SRP0
0x11 -> return SRP1
0x12 -> return SRP2
0x1F -> return SSW
0x1E -> return SSWCI
0x61 -> return SUB
0x00 -> return $ SVTCA DirectionY
0x01 -> return $ SVTCA DirectionX
0x23 -> return SWAP
0x13 -> return SZP0
0x14 -> return SZP1
0x15 -> return SZP2
0x16 -> return SZPS
0x29 -> return UTP
0x70 -> return WCVTF
0x44 -> return WCVTP
0x42 -> return WS
_ -> fail "instruction reader coverage pleaser"
type Point = (F26Dot6, F26Dot6)
data GraphicalState = GraphicalState
{ _stAutoFlip :: !Bool
, _stCutIn :: !F26Dot6
, _stDeltaBase :: !Int32
, _stDeltaShift :: !Int32
, _stDualProjection :: !(Maybe Point)
, _stFreedomVector :: !Point
, _stInstructionControl :: !Bool
, _stLoop :: !Int32
, _stMinDistance :: !F26Dot6
, _stProjectionVector :: !Point
, _stRoundState :: !Int32
, _stRp0 :: !Int32
, _stRp1 :: !Int32
, _stRp2 :: !Int32
, _stScanControl :: !Bool
, _stSingleWidthCutIn :: !F26Dot6
, _stSingleWidthValue :: !F26Dot6
, _stZp0 :: !Int32
, _stZp1 :: !Int32
, _stZp2 :: !Int32
, _stFunctions :: !(M.Map Int32 [ByteCodeProgram])
}
deriving (Eq, Show)
initialState :: GraphicalState
initialState = GraphicalState
{ _stAutoFlip = True
, _stCutIn = 17 / 16
, _stDeltaBase = 9
, _stDeltaShift = 3
, _stDualProjection = Nothing
, _stFreedomVector = (1, 0)
, _stInstructionControl = False
, _stLoop = 1
, _stMinDistance = 1
, _stProjectionVector = (1, 0)
, _stRoundState = 1
, _stRp0 = 0
, _stRp1 = 0
, _stRp2 = 0
, _stScanControl = False
, _stSingleWidthCutIn = 0
, _stSingleWidthValue = 0
, _stZp0 = 1
, _stZp1 = 1
, _stZp2 = 1
, _stFunctions = mempty
}
#if 0
evaluate :: [ByteCodeProgram] -> ()
evaluate instrs =
where
byteExec [] stack = stack
byteExec (If _ : instr) (0:stack) = byteExec instr stack
byteExec (If thenBody : instr) (_:stack) = byteExec (thenBody ++ instr) stack
byteExec (IfElse _ elseBody) (0:stack) = byteExec (elseBody ++ instr) stack
byteExec (IfElse thenBody _) (_:stack) = byteExec (thenBody ++ instr) stack
byteExec (Function functionBody : instr) (id:stack) =
modify (\s -> s { _stFunctions = M.add id functionBody }) >> byteExec instr stack
byteExec (Sequence intructions : rest) stack = go instructions stack >>= byteExec rest
#endif