module Codec.Picture.BitWriter( BoolWriter
, BoolReader
, writeBits
, byteAlignJpg
, getNextBits
, getNextBitJpg
, setDecodedString
, setDecodedStringJpg
, pushByte
, runBoolWriter
, runBoolReader
) where
import Control.Monad( when )
import Control.Monad.ST( ST )
import qualified Control.Monad.Trans.State.Strict as S
import Control.Monad.Trans.Class( MonadTrans( .. ) )
import Data.Word( Word8, Word32 )
import Data.Bits( Bits, (.&.), (.|.), shiftR, shiftL )
import qualified Data.Vector.Storable.Mutable as M
import qualified Data.Vector.Storable as VS
import qualified Data.ByteString as B
(.<<.), (.>>.) :: (Bits a) => a -> Int -> a
(.<<.) = shiftL
(.>>.) = shiftR
type BoolState = (Int, Word8, B.ByteString)
type BoolReader s a = S.StateT BoolState (ST s) a
runBoolReader :: BoolReader s a -> ST s a
runBoolReader action = S.evalStateT action (0, 0, B.empty)
setDecodedString :: B.ByteString -> BoolReader s ()
setDecodedString str = case B.uncons str of
Nothing -> S.put ( 0, 0, B.empty)
Just (v, rest) -> S.put ( 0, v, rest)
byteAlignJpg :: BoolReader s ()
byteAlignJpg = do
(idx, _, chain) <- S.get
when (idx /= 7) (setDecodedStringJpg chain)
getNextBitJpg :: BoolReader s Bool
getNextBitJpg = do
(idx, v, chain) <- S.get
let val = (v .&. (1 `shiftL` idx)) /= 0
if idx == 0
then setDecodedStringJpg chain
else S.put (idx 1, v, chain)
return val
getNextBits :: Int -> BoolReader s Word32
getNextBits count = aux 0 count
where aux acc 0 = return acc
aux acc n = do
bit <- getNextBit
let nextVal | bit = acc .|. (1 .<<. (count n))
| otherwise = acc
aux nextVal (n 1)
getNextBit :: BoolReader s Bool
getNextBit = do
(idx, v, chain) <- S.get
let val = (v .&. (1 `shiftL` idx)) /= 0
if idx == 7
then setDecodedString chain
else S.put (idx + 1, v, chain)
return val
setDecodedStringJpg :: B.ByteString -> BoolReader s ()
setDecodedStringJpg str = case B.uncons str of
Nothing -> S.put (maxBound, 0, B.empty)
Just (0xFF, rest) -> case B.uncons rest of
Nothing -> S.put (maxBound, 0, B.empty)
Just (0x00, afterMarker) -> S.put (7, 0xFF, afterMarker)
Just (_ , afterMarker) -> setDecodedStringJpg afterMarker
Just (v, rest) -> S.put ( 7, v, rest)
defaultBufferSize :: Int
defaultBufferSize = 100 * 1024
runBoolWriter :: BoolWriter s b -> ST s B.ByteString
runBoolWriter writer = do
origMv <- M.new defaultBufferSize
st <- S.execStateT (writer >> flushWriter) (BoolWriteState origMv [] 0 0 0)
st' <- forceBufferFlushing st
return . B.concat $ strings st'
data BoolWriteState s = BoolWriteState
{ wordWrite :: M.MVector s Word8
, strings :: ![B.ByteString]
, writtenWords :: !Int
, bitAcc :: !Word8
, bitReaded :: !Int
}
type BoolWriter s a = S.StateT (BoolWriteState s) (ST s) a
forceBufferFlushing :: BoolWriteState s -> ST s (BoolWriteState s)
forceBufferFlushing st@(BoolWriteState { wordWrite = vec
, writtenWords = count
, strings = lst
}) = do
nmv <- M.new defaultBufferSize
str <- byteStringFromVector vec count
return $ st { wordWrite = nmv
, strings = lst ++ [str]
, writtenWords = 0
}
flushCurrentBuffer :: BoolWriteState s -> ST s (BoolWriteState s)
flushCurrentBuffer st | writtenWords st < M.length (wordWrite st) = return st
flushCurrentBuffer st = forceBufferFlushing st
byteStringFromVector :: M.MVector s Word8 -> Int -> ST s B.ByteString
byteStringFromVector vec size = do
frozen <- VS.unsafeFreeze vec
return . B.pack . take size $ VS.toList frozen
setBitCount :: Word8 -> Int -> BoolWriter s ()
setBitCount acc count = S.modify $ \s ->
s { bitAcc = acc, bitReaded = count }
resetBitCount :: BoolWriter s ()
resetBitCount = setBitCount 0 0
pushByte :: Word8 -> BoolWriter s ()
pushByte v = do
st <- S.get
st'@(BoolWriteState { writtenWords = idx })
<- lift $ flushCurrentBuffer st
lift $ M.write (wordWrite st') idx v
S.put $ st' { writtenWords = idx + 1 }
flushWriter :: BoolWriter s ()
flushWriter = do
st <- S.get
let count = bitReaded st
when (count > 0)
(do let newContext = st { bitAcc = 0, bitReaded = 0 }
S.put newContext
pushByte $ bitAcc st `shiftL` (8 count))
writeBits :: Word32
-> Int
-> BoolWriter s ()
writeBits d c = do
currWord <- S.gets bitAcc
currCount <- S.gets bitReaded
serialize d c currWord currCount
where dumpByte 0xFF = pushByte 0xFF >> pushByte 0x00
dumpByte i = pushByte i
serialize bitData bitCount currentWord count
| bitCount + count == 8 = do
resetBitCount
dumpByte (fromIntegral $ (currentWord .<<. bitCount) .|.
fromIntegral cleanData)
| bitCount + count < 8 =
let newVal = currentWord .<<. bitCount
in setBitCount (newVal .|. fromIntegral cleanData) $ count + bitCount
| otherwise =
let leftBitCount = 8 count :: Int
highPart = cleanData .>>. (bitCount leftBitCount) :: Word32
prevPart = fromIntegral currentWord .<<. leftBitCount :: Word32
nextMask = (1 .<<. (bitCount leftBitCount)) 1 :: Word32
newData = cleanData .&. nextMask :: Word32
newCount = bitCount leftBitCount :: Int
toWrite = fromIntegral $ prevPart .|. highPart :: Word8
in resetBitCount >> dumpByte toWrite >> serialize newData newCount 0 0
where cleanMask = (1 `shiftL` bitCount) 1 :: Word32
cleanData = bitData .&. cleanMask :: Word32