{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE CPP #-}
module Codec.Picture.Jpg.Common
( DctCoefficients
, JpgUnpackerParameter( .. )
, decodeInt
, dcCoefficientDecode
, deQuantize
, decodeRrrrSsss
, zigZagReorderForward
, zigZagReorderForwardv
, zigZagReorder
, inverseDirectCosineTransform
, unpackInt
, unpackMacroBlock
, rasterMap
, decodeMacroBlock
, decodeRestartInterval
, toBlockSize
) where
#if !MIN_VERSION_base(4,8,0)
import Control.Applicative( pure, (<$>) )
#endif
import Control.Monad( when )
import Control.Monad.ST( ST, runST )
import Data.Bits( unsafeShiftL, unsafeShiftR, (.&.) )
import Data.Int( Int16, Int32 )
import Data.Maybe( fromMaybe )
import Data.Word( Word8 )
import qualified Data.Vector.Storable as VS
import qualified Data.Vector.Storable.Mutable as M
import Foreign.Storable ( Storable )
import Codec.Picture.Types
import Codec.Picture.BitWriter
import Codec.Picture.Jpg.Types
import Codec.Picture.Jpg.FastIdct
import Codec.Picture.Jpg.DefaultTable
type DctCoefficients = DcCoefficient
data JpgUnpackerParameter = JpgUnpackerParameter
{ dcHuffmanTree :: !HuffmanPackedTree
, acHuffmanTree :: !HuffmanPackedTree
, componentIndex :: {-# UNPACK #-} !Int
, restartInterval :: {-# UNPACK #-} !Int
, componentWidth :: {-# UNPACK #-} !Int
, componentHeight :: {-# UNPACK #-} !Int
, subSampling :: !(Int, Int)
, coefficientRange :: !(Int, Int)
, successiveApprox :: !(Int, Int)
, readerIndex :: {-# UNPACK #-} !Int
, indiceVector :: {-# UNPACK #-} !Int
, blockIndex :: {-# UNPACK #-} !Int
, blockMcuX :: {-# UNPACK #-} !Int
, blockMcuY :: {-# UNPACK #-} !Int
}
deriving Show
toBlockSize :: Int -> Int
toBlockSize v = (v + 7) `div` 8
decodeRestartInterval :: BoolReader s Int32
decodeRestartInterval = return (-1)
{-# INLINE decodeInt #-}
decodeInt :: Int -> BoolReader s Int32
decodeInt ssss = do
signBit <- getNextBitJpg
let dataRange = 1 `unsafeShiftL` fromIntegral (ssss - 1)
leftBitCount = ssss - 1
if signBit
then (\w -> dataRange + fromIntegral w) <$> unpackInt leftBitCount
else (\w -> 1 - dataRange * 2 + fromIntegral w) <$> unpackInt leftBitCount
decodeRrrrSsss :: HuffmanPackedTree -> BoolReader s (Int, Int)
decodeRrrrSsss tree = do
rrrrssss <- huffmanPackedDecode tree
let rrrr = (rrrrssss `unsafeShiftR` 4) .&. 0xF
ssss = rrrrssss .&. 0xF
pure (fromIntegral rrrr, fromIntegral ssss)
dcCoefficientDecode :: HuffmanPackedTree -> BoolReader s DcCoefficient
dcCoefficientDecode dcTree = do
ssss <- huffmanPackedDecode dcTree
if ssss == 0
then return 0
else fromIntegral <$> decodeInt (fromIntegral ssss)
{-# INLINE deQuantize #-}
deQuantize :: MacroBlock Int16 -> MutableMacroBlock s Int16
-> ST s (MutableMacroBlock s Int16)
deQuantize table block = update 0
where update 64 = return block
update i = do
val <- block `M.unsafeRead` i
let finalValue = val * (table `VS.unsafeIndex` i)
(block `M.unsafeWrite` i) finalValue
update $ i + 1
inverseDirectCosineTransform :: MutableMacroBlock s Int16
-> ST s (MutableMacroBlock s Int16)
inverseDirectCosineTransform mBlock =
fastIdct mBlock >>= mutableLevelShift
zigZagOrder :: MacroBlock Int
zigZagOrder = makeMacroBlock $ concat
[[ 0, 1, 5, 6,14,15,27,28]
,[ 2, 4, 7,13,16,26,29,42]
,[ 3, 8,12,17,25,30,41,43]
,[ 9,11,18,24,31,40,44,53]
,[10,19,23,32,39,45,52,54]
,[20,22,33,38,46,51,55,60]
,[21,34,37,47,50,56,59,61]
,[35,36,48,49,57,58,62,63]
]
zigZagReorderForwardv :: (Storable a, Num a) => VS.Vector a -> VS.Vector a
zigZagReorderForwardv vec = runST $ do
v <- M.new 64
mv <- VS.thaw vec
zigZagReorderForward v mv >>= VS.freeze
zigZagOrderForward :: MacroBlock Int
zigZagOrderForward = VS.generate 64 inv
where inv i = fromMaybe 0 $ VS.findIndex (i ==) zigZagOrder
zigZagReorderForward :: (Storable a)
=> MutableMacroBlock s a
-> MutableMacroBlock s a
-> ST s (MutableMacroBlock s a)
{-# SPECIALIZE INLINE zigZagReorderForward :: MutableMacroBlock s Int32
-> MutableMacroBlock s Int32
-> ST s (MutableMacroBlock s Int32) #-}
{-# SPECIALIZE INLINE zigZagReorderForward :: MutableMacroBlock s Int16
-> MutableMacroBlock s Int16
-> ST s (MutableMacroBlock s Int16) #-}
{-# SPECIALIZE INLINE zigZagReorderForward :: MutableMacroBlock s Word8
-> MutableMacroBlock s Word8
-> ST s (MutableMacroBlock s Word8) #-}
zigZagReorderForward zigzaged block = ordering zigZagOrderForward >> return zigzaged
where ordering !table = reorder (0 :: Int)
where reorder !i | i >= 64 = return ()
reorder i = do
let idx = table `VS.unsafeIndex` i
v <- block `M.unsafeRead` idx
(zigzaged `M.unsafeWrite` i) v
reorder (i + 1)
zigZagReorder :: MutableMacroBlock s Int16 -> MutableMacroBlock s Int16
-> ST s (MutableMacroBlock s Int16)
zigZagReorder zigzaged block = do
let update i = do
let idx = zigZagOrder `VS.unsafeIndex` i
v <- block `M.unsafeRead` idx
(zigzaged `M.unsafeWrite` i) v
reorder 63 = update 63
reorder i = update i >> reorder (i + 1)
reorder (0 :: Int)
return zigzaged
unpackInt :: Int -> BoolReader s Int32
unpackInt = getNextIntJpg
{-# INLINE rasterMap #-}
rasterMap :: (Monad m)
=> Int -> Int -> (Int -> Int -> m ())
-> m ()
rasterMap width height f = liner 0
where liner y | y >= height = return ()
liner y = columner 0
where columner x | x >= width = liner (y + 1)
columner x = f x y >> columner (x + 1)
pixelClamp :: Int16 -> Word8
pixelClamp n = fromIntegral . min 255 $ max 0 n
unpackMacroBlock :: Int
-> Int
-> Int
-> Int
-> Int
-> Int
-> MutableImage s PixelYCbCr8
-> MutableMacroBlock s Int16
-> ST s ()
unpackMacroBlock compCount wCoeff hCoeff compIdx x y
(MutableImage { mutableImageWidth = imgWidth,
mutableImageHeight = imgHeight, mutableImageData = img })
block = rasterMap dctBlockSize dctBlockSize unpacker
where unpacker i j = do
let yBase = y * dctBlockSize + j * hCoeff
compVal <- pixelClamp <$> (block `M.unsafeRead` (i + j * dctBlockSize))
rasterMap wCoeff hCoeff $ \wDup hDup -> do
let xBase = x * dctBlockSize + i * wCoeff
xPos = xBase + wDup
yPos = yBase + hDup
when (xPos < imgWidth && yPos < imgHeight)
(do let mutableIdx = (xPos + yPos * imgWidth) * compCount + compIdx
(img `M.unsafeWrite` mutableIdx) compVal)
decodeMacroBlock :: MacroBlock DctCoefficients
-> MutableMacroBlock s Int16
-> MutableMacroBlock s Int16
-> ST s (MutableMacroBlock s Int16)
decodeMacroBlock quantizationTable zigZagBlock block =
deQuantize quantizationTable block >>= zigZagReorder zigZagBlock
>>= inverseDirectCosineTransform