packages feed

JuicyPixels 3.3.2 → 3.3.3

raw patch · 39 files changed

+4717/−4172 lines, 39 filesdep ~basedep ~primitivePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: base, primitive

API changes (from Hackage documentation)

- Codec.Picture.Bitmap: instance Data.Binary.Class.Binary Codec.Picture.Bitmap.BmpInfoHeader
- Codec.Picture.Bitmap: instance GHC.Show.Show Codec.Picture.Bitmap.BmpInfoHeader
- Codec.Picture.Tiff: instance Codec.Picture.Tiff.Types.BinaryParam Data.ByteString.Internal.ByteString Codec.Picture.Tiff.TiffInfo
+ Codec.Picture: -- as their PixelBaseComponent, HDR image would have Float for instance
+ Codec.Picture: -- | Type of the pixel component, "classical" images would have Word8 type
+ Codec.Picture: dynamicMap :: (forall pixel. Pixel pixel => Image pixel -> a) -> DynamicImage -> a
+ Codec.Picture: dynamicPixelMap :: (forall pixel. Pixel pixel => Image pixel -> Image pixel) -> DynamicImage -> DynamicImage
+ Codec.Picture.Bitmap: instance Data.Binary.Class.Binary Codec.Picture.Bitmap.BmpV5Header
+ Codec.Picture.Bitmap: instance Data.Binary.Class.Binary Codec.Picture.Bitmap.ColorSpaceType
+ Codec.Picture.Bitmap: instance GHC.Classes.Eq Codec.Picture.Bitmap.ColorSpaceType
+ Codec.Picture.Bitmap: instance GHC.Classes.Eq Codec.Picture.Bitmap.RGBABmpFormat
+ Codec.Picture.Bitmap: instance GHC.Classes.Eq Codec.Picture.Bitmap.RGBBmpFormat
+ Codec.Picture.Bitmap: instance GHC.Classes.Eq t => GHC.Classes.Eq (Codec.Picture.Bitmap.Bitfield t)
+ Codec.Picture.Bitmap: instance GHC.Classes.Eq t => GHC.Classes.Eq (Codec.Picture.Bitmap.Bitfields3 t)
+ Codec.Picture.Bitmap: instance GHC.Classes.Eq t => GHC.Classes.Eq (Codec.Picture.Bitmap.Bitfields4 t)
+ Codec.Picture.Bitmap: instance GHC.Show.Show Codec.Picture.Bitmap.BmpV5Header
+ Codec.Picture.Bitmap: instance GHC.Show.Show Codec.Picture.Bitmap.ColorSpaceType
+ Codec.Picture.Bitmap: instance GHC.Show.Show Codec.Picture.Bitmap.IndexedBmpFormat
+ Codec.Picture.Bitmap: instance GHC.Show.Show Codec.Picture.Bitmap.RGBABmpFormat
+ Codec.Picture.Bitmap: instance GHC.Show.Show Codec.Picture.Bitmap.RGBBmpFormat
+ Codec.Picture.Bitmap: instance GHC.Show.Show t => GHC.Show.Show (Codec.Picture.Bitmap.Bitfield t)
+ Codec.Picture.Bitmap: instance GHC.Show.Show t => GHC.Show.Show (Codec.Picture.Bitmap.Bitfields3 t)
+ Codec.Picture.Bitmap: instance GHC.Show.Show t => GHC.Show.Show (Codec.Picture.Bitmap.Bitfields4 t)
+ Codec.Picture.Gif.Internal.LZW: decodeLzw :: ByteString -> Int -> Int -> STVector s Word8 -> BoolReader s ()
+ Codec.Picture.Gif.Internal.LZW: decodeLzwTiff :: ByteString -> STVector s Word8 -> Int -> BoolReader s ()
+ Codec.Picture.Gif.Internal.LZW: instance GHC.Classes.Eq Codec.Picture.Gif.Internal.LZW.TiffVariant
+ Codec.Picture.Gif.Internal.LZWEncoding: lzwEncode :: Int -> Vector Word8 -> ByteString
+ Codec.Picture.Jpg.Internal.Common: JpgUnpackerParameter :: !HuffmanPackedTree -> !HuffmanPackedTree -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> !(Int, Int) -> !(Int, Int) -> !(Int, Int) -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> JpgUnpackerParameter
+ Codec.Picture.Jpg.Internal.Common: [acHuffmanTree] :: JpgUnpackerParameter -> !HuffmanPackedTree
+ Codec.Picture.Jpg.Internal.Common: [blockIndex] :: JpgUnpackerParameter -> {-# UNPACK #-} !Int
+ Codec.Picture.Jpg.Internal.Common: [blockMcuX] :: JpgUnpackerParameter -> {-# UNPACK #-} !Int
+ Codec.Picture.Jpg.Internal.Common: [blockMcuY] :: JpgUnpackerParameter -> {-# UNPACK #-} !Int
+ Codec.Picture.Jpg.Internal.Common: [coefficientRange] :: JpgUnpackerParameter -> !(Int, Int)
+ Codec.Picture.Jpg.Internal.Common: [componentHeight] :: JpgUnpackerParameter -> {-# UNPACK #-} !Int
+ Codec.Picture.Jpg.Internal.Common: [componentIndex] :: JpgUnpackerParameter -> {-# UNPACK #-} !Int
+ Codec.Picture.Jpg.Internal.Common: [componentWidth] :: JpgUnpackerParameter -> {-# UNPACK #-} !Int
+ Codec.Picture.Jpg.Internal.Common: [dcHuffmanTree] :: JpgUnpackerParameter -> !HuffmanPackedTree
+ Codec.Picture.Jpg.Internal.Common: [indiceVector] :: JpgUnpackerParameter -> {-# UNPACK #-} !Int
+ Codec.Picture.Jpg.Internal.Common: [readerIndex] :: JpgUnpackerParameter -> {-# UNPACK #-} !Int
+ Codec.Picture.Jpg.Internal.Common: [restartInterval] :: JpgUnpackerParameter -> {-# UNPACK #-} !Int
+ Codec.Picture.Jpg.Internal.Common: [subSampling] :: JpgUnpackerParameter -> !(Int, Int)
+ Codec.Picture.Jpg.Internal.Common: [successiveApprox] :: JpgUnpackerParameter -> !(Int, Int)
+ Codec.Picture.Jpg.Internal.Common: data JpgUnpackerParameter
+ Codec.Picture.Jpg.Internal.Common: dcCoefficientDecode :: HuffmanPackedTree -> BoolReader s DcCoefficient
+ Codec.Picture.Jpg.Internal.Common: deQuantize :: MacroBlock Int16 -> MutableMacroBlock s Int16 -> ST s (MutableMacroBlock s Int16)
+ Codec.Picture.Jpg.Internal.Common: decodeInt :: Int -> BoolReader s Int32
+ Codec.Picture.Jpg.Internal.Common: decodeMacroBlock :: MacroBlock DctCoefficients -> MutableMacroBlock s Int16 -> MutableMacroBlock s Int16 -> ST s (MutableMacroBlock s Int16)
+ Codec.Picture.Jpg.Internal.Common: decodeRestartInterval :: BoolReader s Int32
+ Codec.Picture.Jpg.Internal.Common: decodeRrrrSsss :: HuffmanPackedTree -> BoolReader s (Int, Int)
+ Codec.Picture.Jpg.Internal.Common: instance GHC.Show.Show Codec.Picture.Jpg.Internal.Common.JpgUnpackerParameter
+ Codec.Picture.Jpg.Internal.Common: inverseDirectCosineTransform :: MutableMacroBlock s Int16 -> ST s (MutableMacroBlock s Int16)
+ Codec.Picture.Jpg.Internal.Common: rasterMap :: Monad m => Int -> Int -> (Int -> Int -> m ()) -> m ()
+ Codec.Picture.Jpg.Internal.Common: toBlockSize :: Int -> Int
+ Codec.Picture.Jpg.Internal.Common: type DctCoefficients = DcCoefficient
+ Codec.Picture.Jpg.Internal.Common: unpackInt :: Int -> BoolReader s Int32
+ Codec.Picture.Jpg.Internal.Common: unpackMacroBlock :: Int -> Int -> Int -> Int -> Int -> Int -> MutableImage s PixelYCbCr8 -> MutableMacroBlock s Int16 -> ST s ()
+ Codec.Picture.Jpg.Internal.Common: zigZagReorder :: MutableMacroBlock s Int16 -> MutableMacroBlock s Int16 -> ST s (MutableMacroBlock s Int16)
+ Codec.Picture.Jpg.Internal.Common: zigZagReorderForward :: Storable a => MutableMacroBlock s a -> MutableMacroBlock s a -> ST s (MutableMacroBlock s a)
+ Codec.Picture.Jpg.Internal.Common: zigZagReorderForwardv :: (Storable a, Num a) => Vector a -> Vector a
+ Codec.Picture.Jpg.Internal.DefaultTable: AcComponent :: DctComponent
+ Codec.Picture.Jpg.Internal.DefaultTable: Branch :: HuffmanTree -> HuffmanTree -> HuffmanTree
+ Codec.Picture.Jpg.Internal.DefaultTable: DcComponent :: DctComponent
+ Codec.Picture.Jpg.Internal.DefaultTable: Empty :: HuffmanTree
+ Codec.Picture.Jpg.Internal.DefaultTable: Leaf :: Word8 -> HuffmanTree
+ Codec.Picture.Jpg.Internal.DefaultTable: buildHuffmanTree :: [[Word8]] -> HuffmanTree
+ Codec.Picture.Jpg.Internal.DefaultTable: data DctComponent
+ Codec.Picture.Jpg.Internal.DefaultTable: data HuffmanTree
+ Codec.Picture.Jpg.Internal.DefaultTable: defaultAcChromaHuffmanTable :: HuffmanTable
+ Codec.Picture.Jpg.Internal.DefaultTable: defaultAcChromaHuffmanTree :: HuffmanTree
+ Codec.Picture.Jpg.Internal.DefaultTable: defaultAcLumaHuffmanTable :: HuffmanTable
+ Codec.Picture.Jpg.Internal.DefaultTable: defaultAcLumaHuffmanTree :: HuffmanTree
+ Codec.Picture.Jpg.Internal.DefaultTable: defaultChromaQuantizationTable :: QuantificationTable
+ Codec.Picture.Jpg.Internal.DefaultTable: defaultDcChromaHuffmanTable :: HuffmanTable
+ Codec.Picture.Jpg.Internal.DefaultTable: defaultDcChromaHuffmanTree :: HuffmanTree
+ Codec.Picture.Jpg.Internal.DefaultTable: defaultDcLumaHuffmanTable :: HuffmanTable
+ Codec.Picture.Jpg.Internal.DefaultTable: defaultDcLumaHuffmanTree :: HuffmanTree
+ Codec.Picture.Jpg.Internal.DefaultTable: defaultLumaQuantizationTable :: QuantificationTable
+ Codec.Picture.Jpg.Internal.DefaultTable: huffmanPackedDecode :: HuffmanPackedTree -> BoolReader s Word8
+ Codec.Picture.Jpg.Internal.DefaultTable: instance GHC.Classes.Eq Codec.Picture.Jpg.Internal.DefaultTable.DctComponent
+ Codec.Picture.Jpg.Internal.DefaultTable: instance GHC.Classes.Eq Codec.Picture.Jpg.Internal.DefaultTable.HuffmanTree
+ Codec.Picture.Jpg.Internal.DefaultTable: instance GHC.Show.Show Codec.Picture.Jpg.Internal.DefaultTable.DctComponent
+ Codec.Picture.Jpg.Internal.DefaultTable: instance GHC.Show.Show Codec.Picture.Jpg.Internal.DefaultTable.HuffmanTree
+ Codec.Picture.Jpg.Internal.DefaultTable: makeInverseTable :: HuffmanTree -> HuffmanWriterCode
+ Codec.Picture.Jpg.Internal.DefaultTable: makeMacroBlock :: Storable a => [a] -> MacroBlock a
+ Codec.Picture.Jpg.Internal.DefaultTable: packHuffmanTree :: HuffmanTree -> HuffmanPackedTree
+ Codec.Picture.Jpg.Internal.DefaultTable: scaleQuantisationMatrix :: Int -> QuantificationTable -> QuantificationTable
+ Codec.Picture.Jpg.Internal.DefaultTable: type HuffmanPackedTree = Vector Word16
+ Codec.Picture.Jpg.Internal.DefaultTable: type HuffmanTable = [[Word8]]
+ Codec.Picture.Jpg.Internal.DefaultTable: type HuffmanWriterCode = Vector (Word8, Word16)
+ Codec.Picture.Jpg.Internal.DefaultTable: type MacroBlock a = Vector a
+ Codec.Picture.Jpg.Internal.DefaultTable: type QuantificationTable = MacroBlock Int16
+ Codec.Picture.Jpg.Internal.FastDct: fastDctLibJpeg :: MutableMacroBlock s Int32 -> MutableMacroBlock s Int16 -> ST s (MutableMacroBlock s Int32)
+ Codec.Picture.Jpg.Internal.FastDct: referenceDct :: MutableMacroBlock s Int32 -> MutableMacroBlock s Int16 -> ST s (MutableMacroBlock s Int32)
+ Codec.Picture.Jpg.Internal.FastIdct: createEmptyMutableMacroBlock :: (Storable a, Num a) => ST s (MutableMacroBlock s a)
+ Codec.Picture.Jpg.Internal.FastIdct: fastIdct :: MutableMacroBlock s Int16 -> ST s (MutableMacroBlock s Int16)
+ Codec.Picture.Jpg.Internal.FastIdct: mutableLevelShift :: MutableMacroBlock s Int16 -> ST s (MutableMacroBlock s Int16)
+ Codec.Picture.Jpg.Internal.FastIdct: type MutableMacroBlock s a = STVector s a
+ Codec.Picture.Jpg.Internal.Metadata: encodeMetadatas :: Metadatas -> [JpgFrame]
+ Codec.Picture.Jpg.Internal.Metadata: extractMetadatas :: JpgJFIFApp0 -> Metadatas
+ Codec.Picture.Jpg.Internal.Progressive: JpgUnpackerParameter :: !HuffmanPackedTree -> !HuffmanPackedTree -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> !(Int, Int) -> !(Int, Int) -> !(Int, Int) -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> JpgUnpackerParameter
+ Codec.Picture.Jpg.Internal.Progressive: [acHuffmanTree] :: JpgUnpackerParameter -> !HuffmanPackedTree
+ Codec.Picture.Jpg.Internal.Progressive: [blockIndex] :: JpgUnpackerParameter -> {-# UNPACK #-} !Int
+ Codec.Picture.Jpg.Internal.Progressive: [blockMcuX] :: JpgUnpackerParameter -> {-# UNPACK #-} !Int
+ Codec.Picture.Jpg.Internal.Progressive: [blockMcuY] :: JpgUnpackerParameter -> {-# UNPACK #-} !Int
+ Codec.Picture.Jpg.Internal.Progressive: [coefficientRange] :: JpgUnpackerParameter -> !(Int, Int)
+ Codec.Picture.Jpg.Internal.Progressive: [componentHeight] :: JpgUnpackerParameter -> {-# UNPACK #-} !Int
+ Codec.Picture.Jpg.Internal.Progressive: [componentIndex] :: JpgUnpackerParameter -> {-# UNPACK #-} !Int
+ Codec.Picture.Jpg.Internal.Progressive: [componentWidth] :: JpgUnpackerParameter -> {-# UNPACK #-} !Int
+ Codec.Picture.Jpg.Internal.Progressive: [dcHuffmanTree] :: JpgUnpackerParameter -> !HuffmanPackedTree
+ Codec.Picture.Jpg.Internal.Progressive: [indiceVector] :: JpgUnpackerParameter -> {-# UNPACK #-} !Int
+ Codec.Picture.Jpg.Internal.Progressive: [readerIndex] :: JpgUnpackerParameter -> {-# UNPACK #-} !Int
+ Codec.Picture.Jpg.Internal.Progressive: [restartInterval] :: JpgUnpackerParameter -> {-# UNPACK #-} !Int
+ Codec.Picture.Jpg.Internal.Progressive: [subSampling] :: JpgUnpackerParameter -> !(Int, Int)
+ Codec.Picture.Jpg.Internal.Progressive: [successiveApprox] :: JpgUnpackerParameter -> !(Int, Int)
+ Codec.Picture.Jpg.Internal.Progressive: data JpgUnpackerParameter
+ Codec.Picture.Jpg.Internal.Progressive: progressiveUnpack :: (Int, Int) -> JpgFrameHeader -> Vector (MacroBlock Int16) -> [([(JpgUnpackerParameter, a)], ByteString)] -> ST s (MutableImage s PixelYCbCr8)
+ Codec.Picture.Jpg.Internal.Types: AdobeUnknown :: AdobeTransform
+ Codec.Picture.Jpg.Internal.Types: AdobeYCbCr :: AdobeTransform
+ Codec.Picture.Jpg.Internal.Types: AdobeYCck :: AdobeTransform
+ Codec.Picture.Jpg.Internal.Types: BaseLineDCT :: JpgImageKind
+ Codec.Picture.Jpg.Internal.Types: JFifPixelsPerCentimeter :: JFifUnit
+ Codec.Picture.Jpg.Internal.Types: JFifPixelsPerInch :: JFifUnit
+ Codec.Picture.Jpg.Internal.Types: JFifUnitUnknown :: JFifUnit
+ Codec.Picture.Jpg.Internal.Types: JpgAdobeAPP14 :: !JpgAdobeApp14 -> JpgFrame
+ Codec.Picture.Jpg.Internal.Types: JpgAdobeApp14 :: !Word16 -> !Word16 -> !Word16 -> !AdobeTransform -> JpgAdobeApp14
+ Codec.Picture.Jpg.Internal.Types: JpgAppFrame :: !Word8 -> ByteString -> JpgFrame
+ Codec.Picture.Jpg.Internal.Types: JpgAppSegment :: Word8 -> JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: JpgBaselineDCTHuffman :: JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: JpgColorSpaceCMYK :: JpgColorSpace
+ Codec.Picture.Jpg.Internal.Types: JpgColorSpaceRGB :: JpgColorSpace
+ Codec.Picture.Jpg.Internal.Types: JpgColorSpaceRGBA :: JpgColorSpace
+ Codec.Picture.Jpg.Internal.Types: JpgColorSpaceY :: JpgColorSpace
+ Codec.Picture.Jpg.Internal.Types: JpgColorSpaceYA :: JpgColorSpace
+ Codec.Picture.Jpg.Internal.Types: JpgColorSpaceYCC :: JpgColorSpace
+ Codec.Picture.Jpg.Internal.Types: JpgColorSpaceYCCA :: JpgColorSpace
+ Codec.Picture.Jpg.Internal.Types: JpgColorSpaceYCCK :: JpgColorSpace
+ Codec.Picture.Jpg.Internal.Types: JpgColorSpaceYCbCr :: JpgColorSpace
+ Codec.Picture.Jpg.Internal.Types: JpgComponent :: !Word8 -> !Word8 -> !Word8 -> !Word8 -> JpgComponent
+ Codec.Picture.Jpg.Internal.Types: JpgDifferentialLosslessArithmetic :: JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: JpgDifferentialLosslessHuffman :: JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: JpgDifferentialProgressiveDCTArithmetic :: JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: JpgDifferentialProgressiveDCTHuffman :: JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: JpgDifferentialSequentialDCTArithmetic :: JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: JpgDifferentialSequentialDCTHuffman :: JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: JpgEndOfImage :: JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: JpgExif :: ![ImageFileDirectory] -> JpgFrame
+ Codec.Picture.Jpg.Internal.Types: JpgExtendedSequentialArithmetic :: JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: JpgExtendedSequentialDCTHuffman :: JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: JpgExtension :: !Word8 -> ByteString -> JpgFrame
+ Codec.Picture.Jpg.Internal.Types: JpgExtensionSegment :: Word8 -> JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: JpgFrameHeader :: !Word16 -> !Word8 -> !Word16 -> !Word16 -> !Word8 -> ![JpgComponent] -> JpgFrameHeader
+ Codec.Picture.Jpg.Internal.Types: JpgHuffmanTable :: ![(JpgHuffmanTableSpec, HuffmanPackedTree)] -> JpgFrame
+ Codec.Picture.Jpg.Internal.Types: JpgHuffmanTableMarker :: JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: JpgHuffmanTableSpec :: !DctComponent -> !Word8 -> !Vector Word8 -> !Vector (Vector Word8) -> JpgHuffmanTableSpec
+ Codec.Picture.Jpg.Internal.Types: JpgImage :: [JpgFrame] -> JpgImage
+ Codec.Picture.Jpg.Internal.Types: JpgIntervalRestart :: !Word16 -> JpgFrame
+ Codec.Picture.Jpg.Internal.Types: JpgJFIF :: !JpgJFIFApp0 -> JpgFrame
+ Codec.Picture.Jpg.Internal.Types: JpgJFIFApp0 :: !JFifUnit -> !Word16 -> !Word16 -> !Maybe Int -> JpgJFIFApp0
+ Codec.Picture.Jpg.Internal.Types: JpgLosslessArithmetic :: JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: JpgLosslessHuffman :: JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: JpgProgressiveDCTArithmetic :: JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: JpgProgressiveDCTHuffman :: JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: JpgQuantTable :: ![JpgQuantTableSpec] -> JpgFrame
+ Codec.Picture.Jpg.Internal.Types: JpgQuantTableSpec :: !Word8 -> !Word8 -> MacroBlock Int16 -> JpgQuantTableSpec
+ Codec.Picture.Jpg.Internal.Types: JpgQuantizationTable :: JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: JpgRestartInterval :: JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: JpgRestartIntervalEnd :: Word8 -> JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: JpgScanBlob :: !JpgScanHeader -> !ByteString -> JpgFrame
+ Codec.Picture.Jpg.Internal.Types: JpgScanHeader :: !Word16 -> !Word8 -> [JpgScanSpecification] -> (Word8, Word8) -> !Word8 -> !Word8 -> JpgScanHeader
+ Codec.Picture.Jpg.Internal.Types: JpgScanSpecification :: !Word8 -> !Word8 -> !Word8 -> JpgScanSpecification
+ Codec.Picture.Jpg.Internal.Types: JpgScans :: !JpgFrameKind -> !JpgFrameHeader -> JpgFrame
+ Codec.Picture.Jpg.Internal.Types: JpgStartOfScan :: JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: ProgressiveDCT :: JpgImageKind
+ Codec.Picture.Jpg.Internal.Types: [_adobeDctVersion] :: JpgAdobeApp14 -> !Word16
+ Codec.Picture.Jpg.Internal.Types: [_adobeFlag0] :: JpgAdobeApp14 -> !Word16
+ Codec.Picture.Jpg.Internal.Types: [_adobeFlag1] :: JpgAdobeApp14 -> !Word16
+ Codec.Picture.Jpg.Internal.Types: [_adobeTransform] :: JpgAdobeApp14 -> !AdobeTransform
+ Codec.Picture.Jpg.Internal.Types: [_jfifDpiX] :: JpgJFIFApp0 -> !Word16
+ Codec.Picture.Jpg.Internal.Types: [_jfifDpiY] :: JpgJFIFApp0 -> !Word16
+ Codec.Picture.Jpg.Internal.Types: [_jfifThumbnail] :: JpgJFIFApp0 -> !Maybe Int
+ Codec.Picture.Jpg.Internal.Types: [_jfifUnit] :: JpgJFIFApp0 -> !JFifUnit
+ Codec.Picture.Jpg.Internal.Types: [acEntropyCodingTable] :: JpgScanSpecification -> !Word8
+ Codec.Picture.Jpg.Internal.Types: [componentIdentifier] :: JpgComponent -> !Word8
+ Codec.Picture.Jpg.Internal.Types: [componentSelector] :: JpgScanSpecification -> !Word8
+ Codec.Picture.Jpg.Internal.Types: [dcEntropyCodingTable] :: JpgScanSpecification -> !Word8
+ Codec.Picture.Jpg.Internal.Types: [horizontalSamplingFactor] :: JpgComponent -> !Word8
+ Codec.Picture.Jpg.Internal.Types: [huffCodes] :: JpgHuffmanTableSpec -> !Vector (Vector Word8)
+ Codec.Picture.Jpg.Internal.Types: [huffSizes] :: JpgHuffmanTableSpec -> !Vector Word8
+ Codec.Picture.Jpg.Internal.Types: [huffmanTableClass] :: JpgHuffmanTableSpec -> !DctComponent
+ Codec.Picture.Jpg.Internal.Types: [huffmanTableDest] :: JpgHuffmanTableSpec -> !Word8
+ Codec.Picture.Jpg.Internal.Types: [jpgComponents] :: JpgFrameHeader -> ![JpgComponent]
+ Codec.Picture.Jpg.Internal.Types: [jpgFrameHeaderLength] :: JpgFrameHeader -> !Word16
+ Codec.Picture.Jpg.Internal.Types: [jpgFrame] :: JpgImage -> [JpgFrame]
+ Codec.Picture.Jpg.Internal.Types: [jpgHeight] :: JpgFrameHeader -> !Word16
+ Codec.Picture.Jpg.Internal.Types: [jpgImageComponentCount] :: JpgFrameHeader -> !Word8
+ Codec.Picture.Jpg.Internal.Types: [jpgSamplePrecision] :: JpgFrameHeader -> !Word8
+ Codec.Picture.Jpg.Internal.Types: [jpgWidth] :: JpgFrameHeader -> !Word16
+ Codec.Picture.Jpg.Internal.Types: [quantDestination] :: JpgQuantTableSpec -> !Word8
+ Codec.Picture.Jpg.Internal.Types: [quantPrecision] :: JpgQuantTableSpec -> !Word8
+ Codec.Picture.Jpg.Internal.Types: [quantTable] :: JpgQuantTableSpec -> MacroBlock Int16
+ Codec.Picture.Jpg.Internal.Types: [quantizationTableDest] :: JpgComponent -> !Word8
+ Codec.Picture.Jpg.Internal.Types: [scanComponentCount] :: JpgScanHeader -> !Word8
+ Codec.Picture.Jpg.Internal.Types: [scanLength] :: JpgScanHeader -> !Word16
+ Codec.Picture.Jpg.Internal.Types: [scans] :: JpgScanHeader -> [JpgScanSpecification]
+ Codec.Picture.Jpg.Internal.Types: [spectralSelection] :: JpgScanHeader -> (Word8, Word8)
+ Codec.Picture.Jpg.Internal.Types: [successiveApproxHigh] :: JpgScanHeader -> !Word8
+ Codec.Picture.Jpg.Internal.Types: [successiveApproxLow] :: JpgScanHeader -> !Word8
+ Codec.Picture.Jpg.Internal.Types: [verticalSamplingFactor] :: JpgComponent -> !Word8
+ Codec.Picture.Jpg.Internal.Types: calculateSize :: SizeCalculable a => a -> Int
+ Codec.Picture.Jpg.Internal.Types: createEmptyMutableMacroBlock :: (Storable a, Num a) => ST s (MutableMacroBlock s a)
+ Codec.Picture.Jpg.Internal.Types: data AdobeTransform
+ Codec.Picture.Jpg.Internal.Types: data JFifUnit
+ Codec.Picture.Jpg.Internal.Types: data JpgAdobeApp14
+ Codec.Picture.Jpg.Internal.Types: data JpgColorSpace
+ Codec.Picture.Jpg.Internal.Types: data JpgComponent
+ Codec.Picture.Jpg.Internal.Types: data JpgFrame
+ Codec.Picture.Jpg.Internal.Types: data JpgFrameHeader
+ Codec.Picture.Jpg.Internal.Types: data JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: data JpgHuffmanTableSpec
+ Codec.Picture.Jpg.Internal.Types: data JpgImage
+ Codec.Picture.Jpg.Internal.Types: data JpgImageKind
+ Codec.Picture.Jpg.Internal.Types: data JpgJFIFApp0
+ Codec.Picture.Jpg.Internal.Types: data JpgQuantTableSpec
+ Codec.Picture.Jpg.Internal.Types: data JpgScanHeader
+ Codec.Picture.Jpg.Internal.Types: data JpgScanSpecification
+ Codec.Picture.Jpg.Internal.Types: dctBlockSize :: Num a => a
+ Codec.Picture.Jpg.Internal.Types: instance (Codec.Picture.Jpg.Internal.Types.SizeCalculable a, Data.Binary.Class.Binary a) => Data.Binary.Class.Binary (Codec.Picture.Jpg.Internal.Types.TableList a)
+ Codec.Picture.Jpg.Internal.Types: instance Codec.Picture.Jpg.Internal.Types.SizeCalculable Codec.Picture.Jpg.Internal.Types.JpgComponent
+ Codec.Picture.Jpg.Internal.Types: instance Codec.Picture.Jpg.Internal.Types.SizeCalculable Codec.Picture.Jpg.Internal.Types.JpgFrameHeader
+ Codec.Picture.Jpg.Internal.Types: instance Codec.Picture.Jpg.Internal.Types.SizeCalculable Codec.Picture.Jpg.Internal.Types.JpgHuffmanTableSpec
+ Codec.Picture.Jpg.Internal.Types: instance Codec.Picture.Jpg.Internal.Types.SizeCalculable Codec.Picture.Jpg.Internal.Types.JpgQuantTableSpec
+ Codec.Picture.Jpg.Internal.Types: instance Codec.Picture.Jpg.Internal.Types.SizeCalculable Codec.Picture.Jpg.Internal.Types.JpgScanHeader
+ Codec.Picture.Jpg.Internal.Types: instance Codec.Picture.Jpg.Internal.Types.SizeCalculable Codec.Picture.Jpg.Internal.Types.JpgScanSpecification
+ Codec.Picture.Jpg.Internal.Types: instance Data.Binary.Class.Binary Codec.Picture.Jpg.Internal.Types.AdobeTransform
+ Codec.Picture.Jpg.Internal.Types: instance Data.Binary.Class.Binary Codec.Picture.Jpg.Internal.Types.JFifUnit
+ Codec.Picture.Jpg.Internal.Types: instance Data.Binary.Class.Binary Codec.Picture.Jpg.Internal.Types.JpgAdobeApp14
+ Codec.Picture.Jpg.Internal.Types: instance Data.Binary.Class.Binary Codec.Picture.Jpg.Internal.Types.JpgComponent
+ Codec.Picture.Jpg.Internal.Types: instance Data.Binary.Class.Binary Codec.Picture.Jpg.Internal.Types.JpgFrameHeader
+ Codec.Picture.Jpg.Internal.Types: instance Data.Binary.Class.Binary Codec.Picture.Jpg.Internal.Types.JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: instance Data.Binary.Class.Binary Codec.Picture.Jpg.Internal.Types.JpgHuffmanTableSpec
+ Codec.Picture.Jpg.Internal.Types: instance Data.Binary.Class.Binary Codec.Picture.Jpg.Internal.Types.JpgImage
+ Codec.Picture.Jpg.Internal.Types: instance Data.Binary.Class.Binary Codec.Picture.Jpg.Internal.Types.JpgJFIFApp0
+ Codec.Picture.Jpg.Internal.Types: instance Data.Binary.Class.Binary Codec.Picture.Jpg.Internal.Types.JpgQuantTableSpec
+ Codec.Picture.Jpg.Internal.Types: instance Data.Binary.Class.Binary Codec.Picture.Jpg.Internal.Types.JpgScanHeader
+ Codec.Picture.Jpg.Internal.Types: instance Data.Binary.Class.Binary Codec.Picture.Jpg.Internal.Types.JpgScanSpecification
+ Codec.Picture.Jpg.Internal.Types: instance Data.Binary.Class.Binary Codec.Picture.Jpg.Internal.Types.RestartInterval
+ Codec.Picture.Jpg.Internal.Types: instance GHC.Classes.Eq Codec.Picture.Jpg.Internal.Types.JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: instance GHC.Show.Show Codec.Picture.Jpg.Internal.Types.AdobeTransform
+ Codec.Picture.Jpg.Internal.Types: instance GHC.Show.Show Codec.Picture.Jpg.Internal.Types.JFifUnit
+ Codec.Picture.Jpg.Internal.Types: instance GHC.Show.Show Codec.Picture.Jpg.Internal.Types.JpgAdobeApp14
+ Codec.Picture.Jpg.Internal.Types: instance GHC.Show.Show Codec.Picture.Jpg.Internal.Types.JpgColorSpace
+ Codec.Picture.Jpg.Internal.Types: instance GHC.Show.Show Codec.Picture.Jpg.Internal.Types.JpgComponent
+ Codec.Picture.Jpg.Internal.Types: instance GHC.Show.Show Codec.Picture.Jpg.Internal.Types.JpgFrame
+ Codec.Picture.Jpg.Internal.Types: instance GHC.Show.Show Codec.Picture.Jpg.Internal.Types.JpgFrameHeader
+ Codec.Picture.Jpg.Internal.Types: instance GHC.Show.Show Codec.Picture.Jpg.Internal.Types.JpgFrameKind
+ Codec.Picture.Jpg.Internal.Types: instance GHC.Show.Show Codec.Picture.Jpg.Internal.Types.JpgHuffmanTableSpec
+ Codec.Picture.Jpg.Internal.Types: instance GHC.Show.Show Codec.Picture.Jpg.Internal.Types.JpgImage
+ Codec.Picture.Jpg.Internal.Types: instance GHC.Show.Show Codec.Picture.Jpg.Internal.Types.JpgJFIFApp0
+ Codec.Picture.Jpg.Internal.Types: instance GHC.Show.Show Codec.Picture.Jpg.Internal.Types.JpgQuantTableSpec
+ Codec.Picture.Jpg.Internal.Types: instance GHC.Show.Show Codec.Picture.Jpg.Internal.Types.JpgScanHeader
+ Codec.Picture.Jpg.Internal.Types: instance GHC.Show.Show Codec.Picture.Jpg.Internal.Types.JpgScanSpecification
+ Codec.Picture.Jpg.Internal.Types: printMacroBlock :: (Storable a, PrintfArg a) => MutableMacroBlock s a -> ST s String
+ Codec.Picture.Jpg.Internal.Types: printPureMacroBlock :: (Storable a, PrintfArg a) => MacroBlock a -> String
+ Codec.Picture.Jpg.Internal.Types: type DcCoefficient = Int16
+ Codec.Picture.Jpg.Internal.Types: type MutableMacroBlock s a = STVector s a
+ Codec.Picture.Metadata: ICCProfile :: !ByteString -> ColorSpace
+ Codec.Picture.Metadata: SRGB :: ColorSpace
+ Codec.Picture.Metadata: WindowsBitmapColorSpace :: !ByteString -> ColorSpace
+ Codec.Picture.Metadata: [ColorSpace] :: Keys ColorSpace
+ Codec.Picture.Metadata: data ColorSpace
+ Codec.Picture.Metadata: instance Control.DeepSeq.NFData Codec.Picture.Metadata.ColorSpace
+ Codec.Picture.Metadata: instance GHC.Classes.Eq Codec.Picture.Metadata.ColorSpace
+ Codec.Picture.Metadata: instance GHC.Show.Show Codec.Picture.Metadata.ColorSpace
+ Codec.Picture.Png.Internal.Export: class PngPaletteSaveable a
+ Codec.Picture.Png.Internal.Export: class PngSavable a
+ Codec.Picture.Png.Internal.Export: encodeDynamicPng :: DynamicImage -> Either String ByteString
+ Codec.Picture.Png.Internal.Export: encodePalettedPng :: PngPaletteSaveable a => Image a -> Image Pixel8 -> Either String ByteString
+ Codec.Picture.Png.Internal.Export: encodePalettedPngWithMetadata :: PngPaletteSaveable a => Metadatas -> Image a -> Image Pixel8 -> Either String ByteString
+ Codec.Picture.Png.Internal.Export: encodePng :: PngSavable a => Image a -> ByteString
+ Codec.Picture.Png.Internal.Export: encodePngWithMetadata :: PngSavable a => Metadatas -> Image a -> ByteString
+ Codec.Picture.Png.Internal.Export: instance Codec.Picture.Png.Internal.Export.PngPaletteSaveable Codec.Picture.Types.PixelRGB8
+ Codec.Picture.Png.Internal.Export: instance Codec.Picture.Png.Internal.Export.PngPaletteSaveable Codec.Picture.Types.PixelRGBA8
+ Codec.Picture.Png.Internal.Export: instance Codec.Picture.Png.Internal.Export.PngSavable Codec.Picture.Types.Pixel16
+ Codec.Picture.Png.Internal.Export: instance Codec.Picture.Png.Internal.Export.PngSavable Codec.Picture.Types.Pixel8
+ Codec.Picture.Png.Internal.Export: instance Codec.Picture.Png.Internal.Export.PngSavable Codec.Picture.Types.PixelRGB16
+ Codec.Picture.Png.Internal.Export: instance Codec.Picture.Png.Internal.Export.PngSavable Codec.Picture.Types.PixelRGB8
+ Codec.Picture.Png.Internal.Export: instance Codec.Picture.Png.Internal.Export.PngSavable Codec.Picture.Types.PixelRGBA16
+ Codec.Picture.Png.Internal.Export: instance Codec.Picture.Png.Internal.Export.PngSavable Codec.Picture.Types.PixelRGBA8
+ Codec.Picture.Png.Internal.Export: instance Codec.Picture.Png.Internal.Export.PngSavable Codec.Picture.Types.PixelYA16
+ Codec.Picture.Png.Internal.Export: instance Codec.Picture.Png.Internal.Export.PngSavable Codec.Picture.Types.PixelYA8
+ Codec.Picture.Png.Internal.Export: writeDynamicPng :: FilePath -> DynamicImage -> IO (Either String Bool)
+ Codec.Picture.Png.Internal.Export: writePng :: PngSavable pixel => FilePath -> Image pixel -> IO ()
+ Codec.Picture.Png.Internal.Metadata: encodeMetadatas :: Metadatas -> [PngRawChunk]
+ Codec.Picture.Png.Internal.Metadata: extractMetadatas :: PngRawImage -> Metadatas
+ Codec.Picture.Png.Internal.Metadata: instance Data.Binary.Class.Binary Codec.Picture.Png.Internal.Metadata.PngText
+ Codec.Picture.Png.Internal.Metadata: instance GHC.Show.Show Codec.Picture.Png.Internal.Metadata.PngText
+ Codec.Picture.Png.Internal.Type: APngAnimationControl :: !Word32 -> !Word32 -> APngAnimationControl
+ Codec.Picture.Png.Internal.Type: APngBlendOver :: APngBlendOp
+ Codec.Picture.Png.Internal.Type: APngBlendSource :: APngBlendOp
+ Codec.Picture.Png.Internal.Type: APngDisposeBackground :: APngFrameDisposal
+ Codec.Picture.Png.Internal.Type: APngDisposeNone :: APngFrameDisposal
+ Codec.Picture.Png.Internal.Type: APngDisposePrevious :: APngFrameDisposal
+ Codec.Picture.Png.Internal.Type: APngFrameControl :: !Word32 -> !Word32 -> !Word32 -> !Word32 -> !Word32 -> !Word16 -> !Word16 -> !APngFrameDisposal -> !APngBlendOp -> APngFrameControl
+ Codec.Picture.Png.Internal.Type: FilterAverage :: PngFilter
+ Codec.Picture.Png.Internal.Type: FilterNone :: PngFilter
+ Codec.Picture.Png.Internal.Type: FilterPaeth :: PngFilter
+ Codec.Picture.Png.Internal.Type: FilterSub :: PngFilter
+ Codec.Picture.Png.Internal.Type: FilterUp :: PngFilter
+ Codec.Picture.Png.Internal.Type: PngChunk :: ByteString -> ChunkSignature -> PngChunk
+ Codec.Picture.Png.Internal.Type: PngGamma :: Double -> PngGamma
+ Codec.Picture.Png.Internal.Type: PngGreyscale :: PngImageType
+ Codec.Picture.Png.Internal.Type: PngGreyscaleWithAlpha :: PngImageType
+ Codec.Picture.Png.Internal.Type: PngIHdr :: !Word32 -> !Word32 -> !Word8 -> !PngImageType -> !Word8 -> !Word8 -> !PngInterlaceMethod -> PngIHdr
+ Codec.Picture.Png.Internal.Type: PngIndexedColor :: PngImageType
+ Codec.Picture.Png.Internal.Type: PngInterlaceAdam7 :: PngInterlaceMethod
+ Codec.Picture.Png.Internal.Type: PngLowLevel :: Image a -> [PngChunk] -> PngLowLevel a
+ Codec.Picture.Png.Internal.Type: PngNoInterlace :: PngInterlaceMethod
+ Codec.Picture.Png.Internal.Type: PngPhysicalDimension :: !Word32 -> !Word32 -> !PngUnit -> PngPhysicalDimension
+ Codec.Picture.Png.Internal.Type: PngRawChunk :: Word32 -> ChunkSignature -> Word32 -> ByteString -> PngRawChunk
+ Codec.Picture.Png.Internal.Type: PngRawImage :: PngIHdr -> [PngRawChunk] -> PngRawImage
+ Codec.Picture.Png.Internal.Type: PngTrueColour :: PngImageType
+ Codec.Picture.Png.Internal.Type: PngTrueColourWithAlpha :: PngImageType
+ Codec.Picture.Png.Internal.Type: PngUnitMeter :: PngUnit
+ Codec.Picture.Png.Internal.Type: PngUnitUnknown :: PngUnit
+ Codec.Picture.Png.Internal.Type: [animationFrameCount] :: APngAnimationControl -> !Word32
+ Codec.Picture.Png.Internal.Type: [animationPlayCount] :: APngAnimationControl -> !Word32
+ Codec.Picture.Png.Internal.Type: [bitDepth] :: PngIHdr -> !Word8
+ Codec.Picture.Png.Internal.Type: [chunkCRC] :: PngRawChunk -> Word32
+ Codec.Picture.Png.Internal.Type: [chunkData] :: PngRawChunk -> ByteString
+ Codec.Picture.Png.Internal.Type: [chunkLength] :: PngRawChunk -> Word32
+ Codec.Picture.Png.Internal.Type: [chunkType] :: PngRawChunk -> ChunkSignature
+ Codec.Picture.Png.Internal.Type: [chunks] :: PngRawImage -> [PngRawChunk]
+ Codec.Picture.Png.Internal.Type: [colourType] :: PngIHdr -> !PngImageType
+ Codec.Picture.Png.Internal.Type: [compressionMethod] :: PngIHdr -> !Word8
+ Codec.Picture.Png.Internal.Type: [filterMethod] :: PngIHdr -> !Word8
+ Codec.Picture.Png.Internal.Type: [frameBlending] :: APngFrameControl -> !APngBlendOp
+ Codec.Picture.Png.Internal.Type: [frameDelayDenuminator] :: APngFrameControl -> !Word16
+ Codec.Picture.Png.Internal.Type: [frameDelayNumerator] :: APngFrameControl -> !Word16
+ Codec.Picture.Png.Internal.Type: [frameDisposal] :: APngFrameControl -> !APngFrameDisposal
+ Codec.Picture.Png.Internal.Type: [frameHeight] :: APngFrameControl -> !Word32
+ Codec.Picture.Png.Internal.Type: [frameLeft] :: APngFrameControl -> !Word32
+ Codec.Picture.Png.Internal.Type: [frameSequenceNum] :: APngFrameControl -> !Word32
+ Codec.Picture.Png.Internal.Type: [frameTop] :: APngFrameControl -> !Word32
+ Codec.Picture.Png.Internal.Type: [frameWidth] :: APngFrameControl -> !Word32
+ Codec.Picture.Png.Internal.Type: [getPngGamma] :: PngGamma -> Double
+ Codec.Picture.Png.Internal.Type: [header] :: PngRawImage -> PngIHdr
+ Codec.Picture.Png.Internal.Type: [height] :: PngIHdr -> !Word32
+ Codec.Picture.Png.Internal.Type: [interlaceMethod] :: PngIHdr -> !PngInterlaceMethod
+ Codec.Picture.Png.Internal.Type: [pngChunkData] :: PngChunk -> ByteString
+ Codec.Picture.Png.Internal.Type: [pngChunkSignature] :: PngChunk -> ChunkSignature
+ Codec.Picture.Png.Internal.Type: [pngChunks] :: PngLowLevel a -> [PngChunk]
+ Codec.Picture.Png.Internal.Type: [pngDpiX] :: PngPhysicalDimension -> !Word32
+ Codec.Picture.Png.Internal.Type: [pngDpiY] :: PngPhysicalDimension -> !Word32
+ Codec.Picture.Png.Internal.Type: [pngImage] :: PngLowLevel a -> Image a
+ Codec.Picture.Png.Internal.Type: [pngUnit] :: PngPhysicalDimension -> !PngUnit
+ Codec.Picture.Png.Internal.Type: [width] :: PngIHdr -> !Word32
+ Codec.Picture.Png.Internal.Type: animationControlSignature :: ChunkSignature
+ Codec.Picture.Png.Internal.Type: chunksWithSig :: PngRawImage -> ChunkSignature -> [ByteString]
+ Codec.Picture.Png.Internal.Type: data APngAnimationControl
+ Codec.Picture.Png.Internal.Type: data APngBlendOp
+ Codec.Picture.Png.Internal.Type: data APngFrameControl
+ Codec.Picture.Png.Internal.Type: data APngFrameDisposal
+ Codec.Picture.Png.Internal.Type: data PngChunk
+ Codec.Picture.Png.Internal.Type: data PngFilter
+ Codec.Picture.Png.Internal.Type: data PngIHdr
+ Codec.Picture.Png.Internal.Type: data PngImageType
+ Codec.Picture.Png.Internal.Type: data PngInterlaceMethod
+ Codec.Picture.Png.Internal.Type: data PngLowLevel a
+ Codec.Picture.Png.Internal.Type: data PngPhysicalDimension
+ Codec.Picture.Png.Internal.Type: data PngRawChunk
+ Codec.Picture.Png.Internal.Type: data PngRawImage
+ Codec.Picture.Png.Internal.Type: data PngUnit
+ Codec.Picture.Png.Internal.Type: gammaSignature :: ChunkSignature
+ Codec.Picture.Png.Internal.Type: iDATSignature :: ChunkSignature
+ Codec.Picture.Png.Internal.Type: iENDSignature :: ChunkSignature
+ Codec.Picture.Png.Internal.Type: instance Data.Binary.Class.Binary Codec.Picture.Png.Internal.Type.PngFilter
+ Codec.Picture.Png.Internal.Type: instance Data.Binary.Class.Binary Codec.Picture.Png.Internal.Type.PngGamma
+ Codec.Picture.Png.Internal.Type: instance Data.Binary.Class.Binary Codec.Picture.Png.Internal.Type.PngIHdr
+ Codec.Picture.Png.Internal.Type: instance Data.Binary.Class.Binary Codec.Picture.Png.Internal.Type.PngImageType
+ Codec.Picture.Png.Internal.Type: instance Data.Binary.Class.Binary Codec.Picture.Png.Internal.Type.PngInterlaceMethod
+ Codec.Picture.Png.Internal.Type: instance Data.Binary.Class.Binary Codec.Picture.Png.Internal.Type.PngPhysicalDimension
+ Codec.Picture.Png.Internal.Type: instance Data.Binary.Class.Binary Codec.Picture.Png.Internal.Type.PngRawChunk
+ Codec.Picture.Png.Internal.Type: instance Data.Binary.Class.Binary Codec.Picture.Png.Internal.Type.PngRawImage
+ Codec.Picture.Png.Internal.Type: instance Data.Binary.Class.Binary Codec.Picture.Png.Internal.Type.PngUnit
+ Codec.Picture.Png.Internal.Type: instance GHC.Enum.Enum Codec.Picture.Png.Internal.Type.PngFilter
+ Codec.Picture.Png.Internal.Type: instance GHC.Enum.Enum Codec.Picture.Png.Internal.Type.PngInterlaceMethod
+ Codec.Picture.Png.Internal.Type: instance GHC.Show.Show Codec.Picture.Png.Internal.Type.APngAnimationControl
+ Codec.Picture.Png.Internal.Type: instance GHC.Show.Show Codec.Picture.Png.Internal.Type.APngBlendOp
+ Codec.Picture.Png.Internal.Type: instance GHC.Show.Show Codec.Picture.Png.Internal.Type.APngFrameControl
+ Codec.Picture.Png.Internal.Type: instance GHC.Show.Show Codec.Picture.Png.Internal.Type.APngFrameDisposal
+ Codec.Picture.Png.Internal.Type: instance GHC.Show.Show Codec.Picture.Png.Internal.Type.PngFilter
+ Codec.Picture.Png.Internal.Type: instance GHC.Show.Show Codec.Picture.Png.Internal.Type.PngIHdr
+ Codec.Picture.Png.Internal.Type: instance GHC.Show.Show Codec.Picture.Png.Internal.Type.PngImageType
+ Codec.Picture.Png.Internal.Type: instance GHC.Show.Show Codec.Picture.Png.Internal.Type.PngInterlaceMethod
+ Codec.Picture.Png.Internal.Type: mkRawChunk :: ChunkSignature -> ByteString -> PngRawChunk
+ Codec.Picture.Png.Internal.Type: newtype PngGamma
+ Codec.Picture.Png.Internal.Type: pHYsSignature :: ChunkSignature
+ Codec.Picture.Png.Internal.Type: pLTESignature :: ChunkSignature
+ Codec.Picture.Png.Internal.Type: parsePalette :: PngRawChunk -> Either String PngPalette
+ Codec.Picture.Png.Internal.Type: pngComputeCrc :: [ByteString] -> Word32
+ Codec.Picture.Png.Internal.Type: tEXtSignature :: ChunkSignature
+ Codec.Picture.Png.Internal.Type: tRNSSignature :: ChunkSignature
+ Codec.Picture.Png.Internal.Type: type ChunkSignature = ByteString
+ Codec.Picture.Png.Internal.Type: type PngPalette = Palette' PixelRGB8
+ Codec.Picture.Png.Internal.Type: zTXtSignature :: ChunkSignature
+ Codec.Picture.Tiff: instance Codec.Picture.Tiff.Internal.Types.BinaryParam Data.ByteString.Internal.ByteString Codec.Picture.Tiff.TiffInfo
+ Codec.Picture.Tiff.Internal.Metadata: encodeTiffStringMetadata :: Metadatas -> [ImageFileDirectory]
+ Codec.Picture.Tiff.Internal.Metadata: exifOffsetIfd :: ImageFileDirectory
+ Codec.Picture.Tiff.Internal.Metadata: extractTiffMetadata :: [ImageFileDirectory] -> Metadatas
+ Codec.Picture.Tiff.Internal.Types: CompressionJPEG :: TiffCompression
+ Codec.Picture.Tiff.Internal.Types: CompressionLZW :: TiffCompression
+ Codec.Picture.Tiff.Internal.Types: CompressionModifiedRLE :: TiffCompression
+ Codec.Picture.Tiff.Internal.Types: CompressionNone :: TiffCompression
+ Codec.Picture.Tiff.Internal.Types: CompressionPackBit :: TiffCompression
+ Codec.Picture.Tiff.Internal.Types: EndianBig :: Endianness
+ Codec.Picture.Tiff.Internal.Types: EndianLittle :: Endianness
+ Codec.Picture.Tiff.Internal.Types: ExtraSampleAssociatedAlpha :: ExtraSample
+ Codec.Picture.Tiff.Internal.Types: ExtraSampleUnassociatedAlpha :: ExtraSample
+ Codec.Picture.Tiff.Internal.Types: ExtraSampleUnspecified :: ExtraSample
+ Codec.Picture.Tiff.Internal.Types: ImageFileDirectory :: !ExifTag -> !IfdType -> !Word32 -> !Word32 -> !ExifData -> ImageFileDirectory
+ Codec.Picture.Tiff.Internal.Types: PlanarConfigContig :: TiffPlanarConfiguration
+ Codec.Picture.Tiff.Internal.Types: PlanarConfigSeparate :: TiffPlanarConfiguration
+ Codec.Picture.Tiff.Internal.Types: PredictorHorizontalDifferencing :: Predictor
+ Codec.Picture.Tiff.Internal.Types: PredictorNone :: Predictor
+ Codec.Picture.Tiff.Internal.Types: TiffCIELab :: TiffColorspace
+ Codec.Picture.Tiff.Internal.Types: TiffCMYK :: TiffColorspace
+ Codec.Picture.Tiff.Internal.Types: TiffHeader :: !Endianness -> {-# UNPACK #-} !Word32 -> TiffHeader
+ Codec.Picture.Tiff.Internal.Types: TiffMonochrome :: TiffColorspace
+ Codec.Picture.Tiff.Internal.Types: TiffMonochromeWhite0 :: TiffColorspace
+ Codec.Picture.Tiff.Internal.Types: TiffPaleted :: TiffColorspace
+ Codec.Picture.Tiff.Internal.Types: TiffRGB :: TiffColorspace
+ Codec.Picture.Tiff.Internal.Types: TiffSampleFloat :: TiffSampleFormat
+ Codec.Picture.Tiff.Internal.Types: TiffSampleInt :: TiffSampleFormat
+ Codec.Picture.Tiff.Internal.Types: TiffSampleUint :: TiffSampleFormat
+ Codec.Picture.Tiff.Internal.Types: TiffSampleUnknown :: TiffSampleFormat
+ Codec.Picture.Tiff.Internal.Types: TiffTransparencyMask :: TiffColorspace
+ Codec.Picture.Tiff.Internal.Types: TiffYCbCr :: TiffColorspace
+ Codec.Picture.Tiff.Internal.Types: TypeAscii :: IfdType
+ Codec.Picture.Tiff.Internal.Types: TypeByte :: IfdType
+ Codec.Picture.Tiff.Internal.Types: TypeDouble :: IfdType
+ Codec.Picture.Tiff.Internal.Types: TypeFloat :: IfdType
+ Codec.Picture.Tiff.Internal.Types: TypeLong :: IfdType
+ Codec.Picture.Tiff.Internal.Types: TypeRational :: IfdType
+ Codec.Picture.Tiff.Internal.Types: TypeSByte :: IfdType
+ Codec.Picture.Tiff.Internal.Types: TypeShort :: IfdType
+ Codec.Picture.Tiff.Internal.Types: TypeSignedLong :: IfdType
+ Codec.Picture.Tiff.Internal.Types: TypeSignedRational :: IfdType
+ Codec.Picture.Tiff.Internal.Types: TypeSignedShort :: IfdType
+ Codec.Picture.Tiff.Internal.Types: TypeUndefined :: IfdType
+ Codec.Picture.Tiff.Internal.Types: [hdrEndianness] :: TiffHeader -> !Endianness
+ Codec.Picture.Tiff.Internal.Types: [hdrOffset] :: TiffHeader -> {-# UNPACK #-} !Word32
+ Codec.Picture.Tiff.Internal.Types: [ifdCount] :: ImageFileDirectory -> !Word32
+ Codec.Picture.Tiff.Internal.Types: [ifdExtended] :: ImageFileDirectory -> !ExifData
+ Codec.Picture.Tiff.Internal.Types: [ifdIdentifier] :: ImageFileDirectory -> !ExifTag
+ Codec.Picture.Tiff.Internal.Types: [ifdOffset] :: ImageFileDirectory -> !Word32
+ Codec.Picture.Tiff.Internal.Types: [ifdType] :: ImageFileDirectory -> !IfdType
+ Codec.Picture.Tiff.Internal.Types: class BinaryParam a b
+ Codec.Picture.Tiff.Internal.Types: codeOfExtraSample :: ExtraSample -> Word16
+ Codec.Picture.Tiff.Internal.Types: constantToPlaneConfiguration :: TiffPlanarConfiguration -> Word16
+ Codec.Picture.Tiff.Internal.Types: data Endianness
+ Codec.Picture.Tiff.Internal.Types: data ExtraSample
+ Codec.Picture.Tiff.Internal.Types: data IfdType
+ Codec.Picture.Tiff.Internal.Types: data ImageFileDirectory
+ Codec.Picture.Tiff.Internal.Types: data Predictor
+ Codec.Picture.Tiff.Internal.Types: data TiffColorspace
+ Codec.Picture.Tiff.Internal.Types: data TiffCompression
+ Codec.Picture.Tiff.Internal.Types: data TiffHeader
+ Codec.Picture.Tiff.Internal.Types: data TiffPlanarConfiguration
+ Codec.Picture.Tiff.Internal.Types: data TiffSampleFormat
+ Codec.Picture.Tiff.Internal.Types: getP :: BinaryParam a b => a -> Get b
+ Codec.Picture.Tiff.Internal.Types: instance Codec.Picture.Tiff.Internal.Types.BinaryParam (Codec.Picture.Tiff.Internal.Types.Endianness, GHC.Types.Int, Codec.Picture.Tiff.Internal.Types.ImageFileDirectory) Codec.Picture.Metadata.Exif.ExifData
+ Codec.Picture.Tiff.Internal.Types: instance Codec.Picture.Tiff.Internal.Types.BinaryParam Codec.Picture.Tiff.Internal.Types.Endianness Codec.Picture.Metadata.Exif.ExifTag
+ Codec.Picture.Tiff.Internal.Types: instance Codec.Picture.Tiff.Internal.Types.BinaryParam Codec.Picture.Tiff.Internal.Types.Endianness Codec.Picture.Tiff.Internal.Types.IfdType
+ Codec.Picture.Tiff.Internal.Types: instance Codec.Picture.Tiff.Internal.Types.BinaryParam Codec.Picture.Tiff.Internal.Types.Endianness Codec.Picture.Tiff.Internal.Types.ImageFileDirectory
+ Codec.Picture.Tiff.Internal.Types: instance Codec.Picture.Tiff.Internal.Types.BinaryParam Codec.Picture.Tiff.Internal.Types.Endianness GHC.Int.Int32
+ Codec.Picture.Tiff.Internal.Types: instance Codec.Picture.Tiff.Internal.Types.BinaryParam Codec.Picture.Tiff.Internal.Types.Endianness GHC.Word.Word16
+ Codec.Picture.Tiff.Internal.Types: instance Codec.Picture.Tiff.Internal.Types.BinaryParam Codec.Picture.Tiff.Internal.Types.Endianness GHC.Word.Word32
+ Codec.Picture.Tiff.Internal.Types: instance Codec.Picture.Tiff.Internal.Types.BinaryParam Codec.Picture.Tiff.Internal.Types.Endianness [Codec.Picture.Tiff.Internal.Types.ImageFileDirectory]
+ Codec.Picture.Tiff.Internal.Types: instance Codec.Picture.Tiff.Internal.Types.BinaryParam Data.ByteString.Internal.ByteString (Codec.Picture.Tiff.Internal.Types.TiffHeader, [[Codec.Picture.Tiff.Internal.Types.ImageFileDirectory]])
+ Codec.Picture.Tiff.Internal.Types: instance Data.Binary.Class.Binary Codec.Picture.Tiff.Internal.Types.Endianness
+ Codec.Picture.Tiff.Internal.Types: instance Data.Binary.Class.Binary Codec.Picture.Tiff.Internal.Types.TiffHeader
+ Codec.Picture.Tiff.Internal.Types: instance GHC.Classes.Eq Codec.Picture.Tiff.Internal.Types.Endianness
+ Codec.Picture.Tiff.Internal.Types: instance GHC.Classes.Eq Codec.Picture.Tiff.Internal.Types.Predictor
+ Codec.Picture.Tiff.Internal.Types: instance GHC.Classes.Eq Codec.Picture.Tiff.Internal.Types.TiffHeader
+ Codec.Picture.Tiff.Internal.Types: instance GHC.Classes.Eq Codec.Picture.Tiff.Internal.Types.TiffSampleFormat
+ Codec.Picture.Tiff.Internal.Types: instance GHC.Show.Show Codec.Picture.Tiff.Internal.Types.Endianness
+ Codec.Picture.Tiff.Internal.Types: instance GHC.Show.Show Codec.Picture.Tiff.Internal.Types.IfdType
+ Codec.Picture.Tiff.Internal.Types: instance GHC.Show.Show Codec.Picture.Tiff.Internal.Types.ImageFileDirectory
+ Codec.Picture.Tiff.Internal.Types: instance GHC.Show.Show Codec.Picture.Tiff.Internal.Types.TiffHeader
+ Codec.Picture.Tiff.Internal.Types: packCompression :: TiffCompression -> Word16
+ Codec.Picture.Tiff.Internal.Types: packPhotometricInterpretation :: TiffColorspace -> Word16
+ Codec.Picture.Tiff.Internal.Types: packSampleFormat :: TiffSampleFormat -> Word32
+ Codec.Picture.Tiff.Internal.Types: planarConfgOfConstant :: Word32 -> Get TiffPlanarConfiguration
+ Codec.Picture.Tiff.Internal.Types: predictorOfConstant :: Word32 -> Get Predictor
+ Codec.Picture.Tiff.Internal.Types: putP :: BinaryParam a b => a -> b -> Put
+ Codec.Picture.Tiff.Internal.Types: unPackCompression :: Word32 -> Get TiffCompression
+ Codec.Picture.Tiff.Internal.Types: unpackPhotometricInterpretation :: Word32 -> Get TiffColorspace
+ Codec.Picture.Tiff.Internal.Types: unpackSampleFormat :: Word32 -> Get TiffSampleFormat
+ Codec.Picture.Tiff.Internal.Types: word16OfTag :: ExifTag -> Word16
+ Codec.Picture.Types: -- PixelRGBA8 for instance
+ Codec.Picture.Types: -- as their PixelBaseComponent, HDR image would have Float for instance
+ Codec.Picture.Types: -- | Primitive type asociated to the current pixel It's Word32 for
- Codec.Picture: ImageCMYK16 :: (Image PixelCMYK16) -> DynamicImage
+ Codec.Picture: ImageCMYK16 :: Image PixelCMYK16 -> DynamicImage
- Codec.Picture: ImageCMYK8 :: (Image PixelCMYK8) -> DynamicImage
+ Codec.Picture: ImageCMYK8 :: Image PixelCMYK8 -> DynamicImage
- Codec.Picture: ImageRGB16 :: (Image PixelRGB16) -> DynamicImage
+ Codec.Picture: ImageRGB16 :: Image PixelRGB16 -> DynamicImage
- Codec.Picture: ImageRGB8 :: (Image PixelRGB8) -> DynamicImage
+ Codec.Picture: ImageRGB8 :: Image PixelRGB8 -> DynamicImage
- Codec.Picture: ImageRGBA16 :: (Image PixelRGBA16) -> DynamicImage
+ Codec.Picture: ImageRGBA16 :: Image PixelRGBA16 -> DynamicImage
- Codec.Picture: ImageRGBA8 :: (Image PixelRGBA8) -> DynamicImage
+ Codec.Picture: ImageRGBA8 :: Image PixelRGBA8 -> DynamicImage
- Codec.Picture: ImageRGBF :: (Image PixelRGBF) -> DynamicImage
+ Codec.Picture: ImageRGBF :: Image PixelRGBF -> DynamicImage
- Codec.Picture: ImageY16 :: (Image Pixel16) -> DynamicImage
+ Codec.Picture: ImageY16 :: Image Pixel16 -> DynamicImage
- Codec.Picture: ImageY32 :: (Image Pixel32) -> DynamicImage
+ Codec.Picture: ImageY32 :: Image Pixel32 -> DynamicImage
- Codec.Picture: ImageY8 :: (Image Pixel8) -> DynamicImage
+ Codec.Picture: ImageY8 :: Image Pixel8 -> DynamicImage
- Codec.Picture: ImageYA16 :: (Image PixelYA16) -> DynamicImage
+ Codec.Picture: ImageYA16 :: Image PixelYA16 -> DynamicImage
- Codec.Picture: ImageYA8 :: (Image PixelYA8) -> DynamicImage
+ Codec.Picture: ImageYA8 :: Image PixelYA8 -> DynamicImage
- Codec.Picture: ImageYCbCr8 :: (Image PixelYCbCr8) -> DynamicImage
+ Codec.Picture: ImageYCbCr8 :: Image PixelYCbCr8 -> DynamicImage
- Codec.Picture: ImageYF :: (Image PixelF) -> DynamicImage
+ Codec.Picture: ImageYF :: Image PixelF -> DynamicImage
- Codec.Picture: encodeBitmap :: forall pixel. (BmpEncodable pixel) => Image pixel -> ByteString
+ Codec.Picture: encodeBitmap :: forall pixel. BmpEncodable pixel => Image pixel -> ByteString
- Codec.Picture: encodeTga :: (TgaSaveable px) => Image px -> ByteString
+ Codec.Picture: encodeTga :: TgaSaveable px => Image px -> ByteString
- Codec.Picture: encodeTiff :: forall px. (TiffSaveable px) => Image px -> ByteString
+ Codec.Picture: encodeTiff :: forall px. TiffSaveable px => Image px -> ByteString
- Codec.Picture: generateFoldImage :: forall a acc. (Pixel a) => (acc -> Int -> Int -> (acc, a)) -> acc -> Int -> Int -> (acc, Image a)
+ Codec.Picture: generateFoldImage :: forall a acc. Pixel a => (acc -> Int -> Int -> (acc, a)) -> acc -> Int -> Int -> (acc, Image a)
- Codec.Picture: generateImage :: forall px. (Pixel px) => (Int -> Int -> px) -> Int -> Int -> Image px
+ Codec.Picture: generateImage :: forall px. Pixel px => (Int -> Int -> px) -> Int -> Int -> Image px
- Codec.Picture: imageFromUnsafePtr :: forall px. (Pixel px, (PixelBaseComponent px) ~ Word8) => Int -> Int -> ForeignPtr Word8 -> Image px
+ Codec.Picture: imageFromUnsafePtr :: forall px. (Pixel px, PixelBaseComponent px ~ Word8) => Int -> Int -> ForeignPtr Word8 -> Image px
- Codec.Picture: writeBitmap :: (BmpEncodable pixel) => FilePath -> Image pixel -> IO ()
+ Codec.Picture: writeBitmap :: BmpEncodable pixel => FilePath -> Image pixel -> IO ()
- Codec.Picture: writePng :: (PngSavable pixel) => FilePath -> Image pixel -> IO ()
+ Codec.Picture: writePng :: PngSavable pixel => FilePath -> Image pixel -> IO ()
- Codec.Picture: writeTga :: (TgaSaveable pixel) => FilePath -> Image pixel -> IO ()
+ Codec.Picture: writeTga :: TgaSaveable pixel => FilePath -> Image pixel -> IO ()
- Codec.Picture: writeTiff :: (TiffSaveable pixel) => FilePath -> Image pixel -> IO ()
+ Codec.Picture: writeTiff :: TiffSaveable pixel => FilePath -> Image pixel -> IO ()
- Codec.Picture.Bitmap: encodeBitmap :: forall pixel. (BmpEncodable pixel) => Image pixel -> ByteString
+ Codec.Picture.Bitmap: encodeBitmap :: forall pixel. BmpEncodable pixel => Image pixel -> ByteString
- Codec.Picture.Bitmap: encodeBitmapWithPaletteAndMetadata :: forall pixel. (BmpEncodable pixel) => Metadatas -> BmpPalette -> Image pixel -> ByteString
+ Codec.Picture.Bitmap: encodeBitmapWithPaletteAndMetadata :: forall pixel. BmpEncodable pixel => Metadatas -> BmpPalette -> Image pixel -> ByteString
- Codec.Picture.Bitmap: writeBitmap :: (BmpEncodable pixel) => FilePath -> Image pixel -> IO ()
+ Codec.Picture.Bitmap: writeBitmap :: BmpEncodable pixel => FilePath -> Image pixel -> IO ()
- Codec.Picture.Jpg: encodeDirectJpegAtQualityWithMetadata :: forall px. (JpgEncodable px) => Word8 -> Metadatas -> Image px -> ByteString
+ Codec.Picture.Jpg: encodeDirectJpegAtQualityWithMetadata :: forall px. JpgEncodable px => Word8 -> Metadatas -> Image px -> ByteString
- Codec.Picture.Metadata: (:=>) :: !(k a) -> a -> Elem k
+ Codec.Picture.Metadata: (:=>) :: !k a -> a -> Elem k
- Codec.Picture.Metadata.Exif: ExifLongs :: !(Vector Word32) -> ExifData
+ Codec.Picture.Metadata.Exif: ExifLongs :: !Vector Word32 -> ExifData
- Codec.Picture.Metadata.Exif: ExifShorts :: !(Vector Word16) -> ExifData
+ Codec.Picture.Metadata.Exif: ExifShorts :: !Vector Word16 -> ExifData
- Codec.Picture.Png: writePng :: (PngSavable pixel) => FilePath -> Image pixel -> IO ()
+ Codec.Picture.Png: writePng :: PngSavable pixel => FilePath -> Image pixel -> IO ()
- Codec.Picture.Tga: encodeTga :: (TgaSaveable px) => Image px -> ByteString
+ Codec.Picture.Tga: encodeTga :: TgaSaveable px => Image px -> ByteString
- Codec.Picture.Tga: writeTga :: (TgaSaveable pixel) => FilePath -> Image pixel -> IO ()
+ Codec.Picture.Tga: writeTga :: TgaSaveable pixel => FilePath -> Image pixel -> IO ()
- Codec.Picture.Tiff: encodeTiff :: forall px. (TiffSaveable px) => Image px -> ByteString
+ Codec.Picture.Tiff: encodeTiff :: forall px. TiffSaveable px => Image px -> ByteString
- Codec.Picture.Tiff: writeTiff :: (TiffSaveable pixel) => FilePath -> Image pixel -> IO ()
+ Codec.Picture.Tiff: writeTiff :: TiffSaveable pixel => FilePath -> Image pixel -> IO ()
- Codec.Picture.Types: ImageCMYK16 :: (Image PixelCMYK16) -> DynamicImage
+ Codec.Picture.Types: ImageCMYK16 :: Image PixelCMYK16 -> DynamicImage
- Codec.Picture.Types: ImageCMYK8 :: (Image PixelCMYK8) -> DynamicImage
+ Codec.Picture.Types: ImageCMYK8 :: Image PixelCMYK8 -> DynamicImage
- Codec.Picture.Types: ImageRGB16 :: (Image PixelRGB16) -> DynamicImage
+ Codec.Picture.Types: ImageRGB16 :: Image PixelRGB16 -> DynamicImage
- Codec.Picture.Types: ImageRGB8 :: (Image PixelRGB8) -> DynamicImage
+ Codec.Picture.Types: ImageRGB8 :: Image PixelRGB8 -> DynamicImage
- Codec.Picture.Types: ImageRGBA16 :: (Image PixelRGBA16) -> DynamicImage
+ Codec.Picture.Types: ImageRGBA16 :: Image PixelRGBA16 -> DynamicImage
- Codec.Picture.Types: ImageRGBA8 :: (Image PixelRGBA8) -> DynamicImage
+ Codec.Picture.Types: ImageRGBA8 :: Image PixelRGBA8 -> DynamicImage
- Codec.Picture.Types: ImageRGBF :: (Image PixelRGBF) -> DynamicImage
+ Codec.Picture.Types: ImageRGBF :: Image PixelRGBF -> DynamicImage
- Codec.Picture.Types: ImageY16 :: (Image Pixel16) -> DynamicImage
+ Codec.Picture.Types: ImageY16 :: Image Pixel16 -> DynamicImage
- Codec.Picture.Types: ImageY32 :: (Image Pixel32) -> DynamicImage
+ Codec.Picture.Types: ImageY32 :: Image Pixel32 -> DynamicImage
- Codec.Picture.Types: ImageY8 :: (Image Pixel8) -> DynamicImage
+ Codec.Picture.Types: ImageY8 :: Image Pixel8 -> DynamicImage
- Codec.Picture.Types: ImageYA16 :: (Image PixelYA16) -> DynamicImage
+ Codec.Picture.Types: ImageYA16 :: Image PixelYA16 -> DynamicImage
- Codec.Picture.Types: ImageYA8 :: (Image PixelYA8) -> DynamicImage
+ Codec.Picture.Types: ImageYA8 :: Image PixelYA8 -> DynamicImage
- Codec.Picture.Types: ImageYCbCr8 :: (Image PixelYCbCr8) -> DynamicImage
+ Codec.Picture.Types: ImageYCbCr8 :: Image PixelYCbCr8 -> DynamicImage
- Codec.Picture.Types: ImageYF :: (Image PixelF) -> DynamicImage
+ Codec.Picture.Types: ImageYF :: Image PixelF -> DynamicImage
- Codec.Picture.Types: Palette' :: !Int -> !(Vector (PixelBaseComponent px)) -> Palette' px
+ Codec.Picture.Types: Palette' :: !Int -> !Vector (PixelBaseComponent px) -> Palette' px
- Codec.Picture.Types: PalettedRGB16 :: (Image Pixel8) -> (Palette' PixelRGB16) -> PalettedImage
+ Codec.Picture.Types: PalettedRGB16 :: Image Pixel8 -> Palette' PixelRGB16 -> PalettedImage
- Codec.Picture.Types: PalettedRGB8 :: (Image Pixel8) -> (Palette' PixelRGB8) -> PalettedImage
+ Codec.Picture.Types: PalettedRGB8 :: Image Pixel8 -> Palette' PixelRGB8 -> PalettedImage
- Codec.Picture.Types: PalettedRGBA8 :: (Image Pixel8) -> (Palette' PixelRGBA8) -> PalettedImage
+ Codec.Picture.Types: PalettedRGBA8 :: Image Pixel8 -> Palette' PixelRGBA8 -> PalettedImage
- Codec.Picture.Types: PalettedY8 :: (Image Pixel8) -> (Palette' Pixel8) -> PalettedImage
+ Codec.Picture.Types: PalettedY8 :: Image Pixel8 -> Palette' Pixel8 -> PalettedImage
- Codec.Picture.Types: [_paletteData] :: Palette' px -> !(Vector (PixelBaseComponent px))
+ Codec.Picture.Types: [_paletteData] :: Palette' px -> !Vector (PixelBaseComponent px)
- Codec.Picture.Types: dropAlphaLayer :: (TransparentPixel a b) => Image a -> Image b
+ Codec.Picture.Types: dropAlphaLayer :: TransparentPixel a b => Image a -> Image b
- Codec.Picture.Types: dynamicMap :: (forall pixel. (Pixel pixel) => Image pixel -> a) -> DynamicImage -> a
+ Codec.Picture.Types: dynamicMap :: (forall pixel. Pixel pixel => Image pixel -> a) -> DynamicImage -> a
- Codec.Picture.Types: dynamicPixelMap :: (forall pixel. (Pixel pixel) => Image pixel -> Image pixel) -> DynamicImage -> DynamicImage
+ Codec.Picture.Types: dynamicPixelMap :: (forall pixel. Pixel pixel => Image pixel -> Image pixel) -> DynamicImage -> DynamicImage
- Codec.Picture.Types: generateFoldImage :: forall a acc. (Pixel a) => (acc -> Int -> Int -> (acc, a)) -> acc -> Int -> Int -> (acc, Image a)
+ Codec.Picture.Types: generateFoldImage :: forall a acc. Pixel a => (acc -> Int -> Int -> (acc, a)) -> acc -> Int -> Int -> (acc, Image a)
- Codec.Picture.Types: generateImage :: forall px. (Pixel px) => (Int -> Int -> px) -> Int -> Int -> Image px
+ Codec.Picture.Types: generateImage :: forall px. Pixel px => (Int -> Int -> px) -> Int -> Int -> Image px
- Codec.Picture.Types: pixelFold :: forall acc pixel. (Pixel pixel) => (acc -> Int -> Int -> pixel -> acc) -> acc -> Image pixel -> acc
+ Codec.Picture.Types: pixelFold :: forall acc pixel. Pixel pixel => (acc -> Int -> Int -> pixel -> acc) -> acc -> Image pixel -> acc
- Codec.Picture.Types: zipPixelComponent3 :: forall px. (Storable (PixelBaseComponent px)) => (PixelBaseComponent px -> PixelBaseComponent px -> PixelBaseComponent px -> PixelBaseComponent px) -> Image px -> Image px -> Image px -> Image px
+ Codec.Picture.Types: zipPixelComponent3 :: forall px. Storable (PixelBaseComponent px) => (PixelBaseComponent px -> PixelBaseComponent px -> PixelBaseComponent px -> PixelBaseComponent px) -> Image px -> Image px -> Image px -> Image px

Files

JuicyPixels.cabal view
@@ -1,5 +1,5 @@ Name:                JuicyPixels
-Version:             3.3.2
+Version:             3.3.3
 Synopsis:            Picture loading/serialization (in png, jpeg, bitmap, gif, tga, tiff and radiance)
 Description:
     <<data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAADABAMAAACg8nE0AAAAElBMVEUAAABJqDSTWEL/qyb///8AAABH/1GTAAAAAXRSTlMAQObYZgAAAN5JREFUeF7s1sEJgFAQxFBbsAV72v5bEVYWPwT/XDxmCsi7zvHXavYREBDI3XP2GgICqBBYuwIC+/rVayPUAyAg0HvIXBcQoDFDGnUBgWQQ2Bx3AYFaRoBpAQHWb3bt2ARgGAiCYFFuwf3X5HA/McgGJWI2FdykCv4aBYzmKwDwvl6NVmUAAK2vlwEALK7fo88GANB6HQsAAAAAAAAA7P94AQCzswEAAAAAAAAAAAAAAAAAAICzh4UAO4zWAYBfRutHA4Bn5C69JhowAMGoBaMWDG0wCkbBKBgFo2AUAACPmegUST/IJAAAAABJRU5ErkJggg==>>
@@ -28,7 +28,7 @@ Source-Repository this
     Type:      git
     Location:  git://github.com/Twinside/Juicy.Pixels.git
-    Tag:       v3.3.2
+    Tag:       v3.3.3
 
 Flag Mmap
     Description: Enable the file loading via mmap (memory map)
@@ -49,7 +49,21 @@                     Codec.Picture.Metadata.Exif,
                     Codec.Picture.Saving,
                     Codec.Picture.Types,
-                    Codec.Picture.ColorQuant
+                    Codec.Picture.ColorQuant,
+                    Codec.Picture.Jpg.Internal.DefaultTable,
+                    Codec.Picture.Jpg.Internal.Metadata,
+                    Codec.Picture.Jpg.Internal.FastIdct,
+                    Codec.Picture.Jpg.Internal.FastDct,
+                    Codec.Picture.Jpg.Internal.Types,
+                    Codec.Picture.Jpg.Internal.Common,
+                    Codec.Picture.Jpg.Internal.Progressive,
+                    Codec.Picture.Gif.Internal.LZW,
+                    Codec.Picture.Gif.Internal.LZWEncoding,
+                    Codec.Picture.Png.Internal.Export,
+                    Codec.Picture.Png.Internal.Type,
+                    Codec.Picture.Png.Internal.Metadata,
+                    Codec.Picture.Tiff.Internal.Metadata,
+                    Codec.Picture.Tiff.Internal.Types
 
   Ghc-options: -O3 -Wall
   Build-depends: base                >= 4.8     && < 6,
@@ -64,21 +78,7 @@                  containers          >= 0.4.2   && < 0.7
 
   -- Modules not exported by this package.
-  Other-modules: Codec.Picture.Jpg.DefaultTable,
-                 Codec.Picture.Jpg.Metadata,
-                 Codec.Picture.Jpg.FastIdct,
-                 Codec.Picture.Jpg.FastDct,
-                 Codec.Picture.Jpg.Types,
-                 Codec.Picture.Jpg.Common,
-                 Codec.Picture.Jpg.Progressive,
-                 Codec.Picture.Gif.LZW,
-                 Codec.Picture.Gif.LZWEncoding,
-                 Codec.Picture.Png.Export,
-                 Codec.Picture.Png.Type,
-                 Codec.Picture.Png.Metadata,
-                 Codec.Picture.Tiff.Metadata,
-                 Codec.Picture.Tiff.Types,
-                 Codec.Picture.BitWriter,
+  Other-modules: Codec.Picture.BitWriter,
                  Codec.Picture.InternalHelper,
                  Codec.Picture.VectorByteConversion
 
README.md view
@@ -51,11 +51,13 @@         * in a gAMA field : 'Gamma'
         * DPI information in a pHYs chunk.
 
- - Bitmap (.bmp) (mainly used as a debug output format)
+ - Bitmap (.bmp)
     * Reading
-        - 32bits (RGBA) images
-        - 24bits (RGB) images
-        - 8bits (greyscale & paletted) images
+        - 16 or 32 bit RGBA images
+        - 16, 24, 32 bit RGB images
+        - 1, 4, or 8 bit (greyscale & paletted) images
+        - RLE encoded or uncompressed
+        - Windows 2.0/3.1/95/98 style bitmaps all supported
 
     * Writing
         - 32bits (RGBA) per pixel images
changelog view
@@ -1,6 +1,14 @@ Change log
 ==========
 
+v3.3.3 December 2018
+--------------------
+
+ * Enhanced: loading of bitmap format (thanks to CLowcay)
+ * Refactoring: exposing more internal modules (thanks to wyager)
+ * Refactoring: exposing dynamicMap & dynamicPixelMap
+   through `Codec.Picture` (thnks to LightAndLight)
+
 v3.3.2 October 2018
 -------------------
 
src/Codec/Picture.hs view
@@ -23,6 +23,8 @@                      , decodeImageWithMetadata
                      , decodeImageWithPaletteAndMetadata
                      , pixelMap
+                     , dynamicMap
+                     , dynamicPixelMap
                      , generateImage
                      , generateFoldImage
                      , withImage
src/Codec/Picture/Bitmap.hs view
@@ -25,7 +25,7 @@ #endif
 
 import Control.Arrow( first )
-import Control.Monad( replicateM, when, foldM_, forM_ )
+import Control.Monad( replicateM, when, foldM_, forM_, void )
 import Control.Monad.ST ( ST, runST )
 import Data.Maybe( fromMaybe )
 import qualified Data.Vector.Storable as VS
@@ -33,6 +33,7 @@ import Data.Binary( Binary( .. ) )
 import Data.Binary.Put( Put
                       , runPut
+                      , putInt32le
                       , putWord16le
                       , putWord32le
                       , putByteString 
@@ -42,14 +43,18 @@                       , getWord8
                       , getWord16le 
                       , getWord32le
-                      , getWord32be
+                      , getInt32le
+                      , getByteString
                       , bytesRead
                       , skip
+                      , label
                       )
 
+import Data.Bits
 import Data.Int( Int32 )
 import Data.Word( Word32, Word16, Word8 )
 import qualified Data.ByteString as B
+import qualified Data.ByteString.Internal as BI
 import qualified Data.ByteString.Lazy as L
 
 import Codec.Picture.InternalHelper
@@ -93,8 +98,43 @@             , dataOffset = offset
             }
 
+-- | The type of color space declared in a Windows BMP file.
+data ColorSpaceType = CalibratedRGB
+                    | DeviceDependentRGB
+                    | DeviceDependentCMYK
+                    | SRGB
+                    | WindowsColorSpace
+                    | ProfileEmbedded
+                    | ProfileLinked
+                    | UnknownColorSpace Word32
+                    deriving (Eq, Show)
 
-data BmpInfoHeader = BmpInfoHeader
+-- | BITMAPxHEADER with compatibility up to V5. This header was first introduced
+-- with Windows 2.0 as the BITMAPCOREHEADER, and was later extended in Windows
+-- 3.1, Windows 95 and Windows 98. The original BITMAPCOREHEADER includes all
+-- fields up to 'bitPerPixel'. The Windows 3.1 BITMAPINFOHEADER adds all the
+-- fields up to 'importantColors'.
+--
+-- Some Windows 3.1 bitmaps with 16 or 32 bits per pixel might also have three
+-- bitmasks following the BITMAPINFOHEADER. These bitmasks were later
+-- incorporated into the bitmap header structure in the unreleased
+-- BITMAPV2INFOHEADER. The (also unreleased) BITMAPV3INFOHEADER added another
+-- bitmask for an alpha channel.
+--
+-- The later Windows 95 and Windows 98 extensions to the BITMAPINFOHEADER extend
+-- the BITMAPV3INFOHEADER, adding support for color correction.
+--
+--  * BITMAPV4HEADER (Windows 95) may include a simple color profile in a
+--      proprietary format. The fields in this color profile (which includes gamma
+--      values) are not to be used unless the 'colorSpaceType' field is
+--      'CalibratedRGB'.
+--
+--  * BITMAPV5HEADER (Windows 98) adds support for an ICC color profile. The
+--      presence of an ICC color profile is indicated by setting the 'colorSpaceType'
+--      field to 'ProfileEmbedded' or 'ProfileLinked'. If it is 'ProfileLinked' then
+--      the profile data is actually a Windows-1252 encoded string containing the
+--      fully qualified path to an ICC color profile.
+data BmpV5Header = BmpV5Header
     { size              :: !Word32 -- Header size in bytes
     , width             :: !Int32
     , height            :: !Int32
@@ -102,67 +142,224 @@     , bitPerPixel       :: !Word16
     , bitmapCompression :: !Word32
     , byteImageSize     :: !Word32
-    , xResolution       :: !Int32 -- ^ Pixels per meter
-    , yResolution       :: !Int32 -- ^ Pixels per meter
-    , colorCount        :: !Word32
+    , xResolution       :: !Int32  -- ^ Pixels per meter
+    , yResolution       :: !Int32  -- ^ Pixels per meter
+    , colorCount        :: !Word32 -- ^ Number of colors in the palette
     , importantColours  :: !Word32
+    -- Fields added to the header in V2
+    , redMask           :: !Word32 -- ^ Red bitfield mask, set to 0 if not used
+    , greenMask         :: !Word32 -- ^ Green bitfield mask, set to 0 if not used
+    , blueMask          :: !Word32 -- ^ Blue bitfield mask, set to 0 if not used
+    -- Fields added to the header in V3
+    , alphaMask         :: !Word32 -- ^ Alpha bitfield mask, set to 0 if not used
+    -- Fields added to the header in V4
+    , colorSpaceType    :: !ColorSpaceType
+    , colorSpace        :: !B.ByteString -- ^ Windows color space, not decoded
+    -- Fields added to the header in V5
+    , iccIntent         :: !Word32
+    , iccProfileData    :: !Word32
+    , iccProfileSize    :: !Word32
     }
     deriving Show
 
-sizeofBmpHeader, sizeofBmpInfo  :: Word32
+-- | Size of the Windows BITMAPV4INFOHEADER color space information.
+sizeofColorProfile :: Int
+sizeofColorProfile = 48
+
+-- | Sizes of basic BMP headers.
+sizeofBmpHeader, sizeofBmpCoreHeader, sizeofBmpInfoHeader :: Word32
 sizeofBmpHeader = 2 + 4 + 2 + 2 + 4
-sizeofBmpInfo = 3 * 4 + 2 * 2 + 6 * 4
+sizeofBmpCoreHeader = 12
+sizeofBmpInfoHeader = 40
 
-instance Binary BmpInfoHeader where
+-- | Sizes of extended BMP headers.
+sizeofBmpV2Header, sizeofBmpV3Header, sizeofBmpV4Header, sizeofBmpV5Header :: Word32
+sizeofBmpV2Header = 52
+sizeofBmpV3Header = 56
+sizeofBmpV4Header = 108
+sizeofBmpV5Header = 124
+
+instance Binary ColorSpaceType where
+    put CalibratedRGB         = putWord32le 0
+    put DeviceDependentRGB    = putWord32le 1
+    put DeviceDependentCMYK   = putWord32le 2
+    put ProfileEmbedded       = putWord32le 0x4D424544
+    put ProfileLinked         = putWord32le 0x4C494E4B
+    put SRGB                  = putWord32le 0x73524742
+    put WindowsColorSpace     = putWord32le 0x57696E20
+    put (UnknownColorSpace x) = putWord32le x
+    get = do
+      w <- getWord32le
+      return $ case w of
+        0          -> CalibratedRGB
+        1          -> DeviceDependentRGB
+        2          -> DeviceDependentCMYK
+        0x4D424544 -> ProfileEmbedded
+        0x4C494E4B -> ProfileLinked
+        0x73524742 -> SRGB
+        0x57696E20 -> WindowsColorSpace
+        _          -> UnknownColorSpace w
+
+instance Binary BmpV5Header where
     put hdr = do
         putWord32le $ size hdr
-        putWord32le . fromIntegral $ width hdr
-        putWord32le . fromIntegral $ height hdr
-        putWord16le $ planes hdr
-        putWord16le $ bitPerPixel hdr
-        putWord32le $ bitmapCompression hdr
-        putWord32le $ byteImageSize hdr
-        putWord32le . fromIntegral $ xResolution hdr
-        putWord32le . fromIntegral $ yResolution hdr
-        putWord32le $ colorCount hdr
-        putWord32le $ importantColours hdr
 
+        if (size hdr == sizeofBmpCoreHeader) then do
+          putWord16le . fromIntegral $ width hdr
+          putWord16le . fromIntegral $ height hdr
+          putWord16le $ planes hdr
+          putWord16le $ bitPerPixel hdr
+        else do
+          putInt32le $ width hdr
+          putInt32le $ height hdr
+          putWord16le $ planes hdr
+          putWord16le $ bitPerPixel hdr
+
+        when (size hdr > sizeofBmpCoreHeader) $ do
+          putWord32le $ bitmapCompression hdr
+          putWord32le $ byteImageSize hdr
+          putInt32le $ xResolution hdr
+          putInt32le $ yResolution hdr
+          putWord32le $ colorCount hdr
+          putWord32le $ importantColours hdr
+
+        when (size hdr > sizeofBmpInfoHeader || bitmapCompression hdr == 3) $ do
+          putWord32le $ redMask hdr
+          putWord32le $ greenMask hdr
+          putWord32le $ blueMask hdr
+
+        when (size hdr > sizeofBmpV2Header) $
+          putWord32le $ alphaMask hdr
+
+        when (size hdr > sizeofBmpV3Header) $ do
+          put $ colorSpaceType hdr
+          putByteString $ colorSpace hdr
+
+        when (size hdr > sizeofBmpV4Header) $ do
+          put $ iccIntent hdr
+          putWord32le $ iccProfileData hdr
+          putWord32le $ iccProfileSize hdr
+          putWord32le 0 -- reserved field
+
     get = do
-        readSize <- getWord32le
-        readWidth <- fromIntegral <$> getWord32le
-        readHeight <- fromIntegral <$> getWord32le
-        readPlanes <- getWord16le
-        readBitPerPixel <- getWord16le
-        readBitmapCompression <- getWord32le
-        readByteImageSize <- getWord32le
-        readXResolution <- fromIntegral <$> getWord32le
-        readYResolution <- fromIntegral <$> getWord32le
-        readColorCount <- getWord32le
-        readImportantColours <- getWord32le
-        return BmpInfoHeader {
-            size = readSize,
-            width = readWidth,
-            height = readHeight,
-            planes = readPlanes,
-            bitPerPixel = readBitPerPixel,
-            bitmapCompression = readBitmapCompression,
-            byteImageSize = readByteImageSize,
-            xResolution = readXResolution,
-            yResolution = readYResolution,
-            colorCount = readColorCount,
-            importantColours = readImportantColours
-        }
+      readSize <- getWord32le
+      if readSize == sizeofBmpCoreHeader
+        then getBitmapCoreHeader readSize
+        else getBitmapInfoHeader readSize
 
+      where
+        getBitmapCoreHeader readSize = do
+          readWidth <- getWord16le
+          readHeight <- getWord16le
+          readPlanes <- getWord16le
+          readBitPerPixel <- getWord16le
+          return BmpV5Header {
+              size = readSize,
+              width = fromIntegral readWidth,
+              height = fromIntegral readHeight,
+              planes = readPlanes,
+              bitPerPixel = readBitPerPixel,
+              bitmapCompression = 0,
+              byteImageSize = 0,
+              xResolution = 2835,
+              yResolution = 2835,
+              colorCount = 2 ^ readBitPerPixel,
+              importantColours = 0,
+              redMask = 0,
+              greenMask = 0,
+              blueMask = 0,
+              alphaMask = 0,
+              colorSpaceType = DeviceDependentRGB,
+              colorSpace = B.empty,
+              iccIntent = 0,
+              iccProfileData = 0,
+              iccProfileSize = 0
+          }
+
+        getBitmapInfoHeader readSize = do
+          readWidth <- getInt32le
+          readHeight <- getInt32le
+          readPlanes <- getWord16le
+          readBitPerPixel <- getWord16le
+          readBitmapCompression <- getWord32le
+          readByteImageSize <- getWord32le
+          readXResolution <- getInt32le
+          readYResolution <- getInt32le
+          readColorCount <- getWord32le
+          readImportantColours <- getWord32le
+
+          (readRedMask, readGreenMask, readBlueMask) <-
+            if readSize == sizeofBmpInfoHeader && readBitmapCompression /= 3
+              then return (0, 0, 0)
+              else do
+                -- fields added to the header in V2, but sometimes present
+                -- immediately after a plain BITMAPINFOHEADER
+                innerReadRedMask <- getWord32le
+                innerReadGreenMask <- getWord32le
+                innerReadBlueMask <- getWord32le
+                return (innerReadRedMask, innerReadGreenMask, innerReadBlueMask)
+
+          -- field added in V3 (undocumented)
+          readAlphaMask <- if readSize < sizeofBmpV3Header then return 0 else getWord32le
+
+          (readColorSpaceType, readColorSpace) <-
+            if readSize < sizeofBmpV4Header
+              then return (DeviceDependentRGB, B.empty)
+              else do
+                -- fields added in V4 (Windows 95)
+                csType <- get
+                cs <- getByteString sizeofColorProfile
+                return (csType, cs)
+
+          (readIccIntent, readIccProfileData, readIccProfileSize) <-
+            if readSize < sizeofBmpV5Header
+              then return (0, 0, 0)
+              else do
+                -- fields added in V5 (Windows 98)
+                innerIccIntent <- getWord32le
+                innerIccProfileData <- getWord32le
+                innerIccProfileSize <- getWord32le
+                void getWord32le -- reserved field
+                return (innerIccIntent, innerIccProfileData, innerIccProfileSize)
+
+          return BmpV5Header {
+              size = readSize,
+              width = readWidth,
+              height = readHeight,
+              planes = readPlanes,
+              bitPerPixel = readBitPerPixel,
+              bitmapCompression = readBitmapCompression,
+              byteImageSize = readByteImageSize,
+              xResolution = readXResolution,
+              yResolution = readYResolution,
+              colorCount = readColorCount,
+              importantColours = readImportantColours,
+              redMask = readRedMask,
+              greenMask = readGreenMask,
+              blueMask = readBlueMask,
+              alphaMask = readAlphaMask,
+              colorSpaceType = readColorSpaceType,
+              colorSpace = readColorSpace,
+              iccIntent = readIccIntent,
+              iccProfileData = readIccProfileData,
+              iccProfileSize = readIccProfileSize
+          }
+
 newtype BmpPalette = BmpPalette [(Word8, Word8, Word8, Word8)]
 
 putPalette :: BmpPalette -> Put
 putPalette (BmpPalette p) = mapM_ (\(r, g, b, a) -> put r >> put g >> put b >> put a) p
 
+putICCProfile :: Maybe B.ByteString -> Put
+putICCProfile Nothing = return ()
+putICCProfile (Just bytes) = put bytes
+
 -- | All the instance of this class can be written as a bitmap file
 -- using this library.
 class BmpEncodable pixel where
     bitsPerPixel   :: pixel -> Int
     bmpEncode      :: Image pixel -> Put
+    hasAlpha       :: Image pixel -> Bool
     defaultPalette :: pixel -> BmpPalette
     defaultPalette _ = BmpPalette []
 
@@ -175,6 +372,7 @@            inner (ix + 1) (n - 1)
 
 instance BmpEncodable Pixel8 where
+    hasAlpha _ = False
     defaultPalette _ = BmpPalette [(x,x,x, 255) | x <- [0 .. 255]]
     bitsPerPixel _ = 8
     bmpEncode (Image {imageWidth = w, imageHeight = h, imageData = arr}) =
@@ -200,6 +398,7 @@                   VS.unsafeFreeze buff
 
 instance BmpEncodable PixelRGBA8 where
+    hasAlpha _ = True
     bitsPerPixel _ = 32
     bmpEncode (Image {imageWidth = w, imageHeight = h, imageData = arr}) = 
       forM_ [h - 1, h - 2 .. 0] $ \l -> putVector $ runST $ putLine l
@@ -228,6 +427,7 @@             VS.unsafeFreeze buff
 
 instance BmpEncodable PixelRGB8 where
+    hasAlpha _ = False
     bitsPerPixel _ = 24
     bmpEncode (Image {imageWidth = w, imageHeight = h, imageData = arr}) =
        forM_ [h - 1, h - 2 .. 0] $ \l -> putVector $ runST $ putLine l
@@ -255,8 +455,72 @@               inner 0 0 initialIndex
               VS.unsafeFreeze buff
 
-decodeImageRGBA8 :: BmpInfoHeader -> (Int, Int, Int, Int) -> B.ByteString -> Image PixelRGBA8
-decodeImageRGBA8 (BmpInfoHeader { width = w, height = h }) (posR, posG, posB, posA) str = Image wi hi stArray where
+-- | Information required to extract data from a bitfield.
+data Bitfield t = Bitfield
+    { bfMask :: !t            -- ^ The original bitmask.
+    , bfShift :: !Int         -- ^ The computed number of bits to shift right.
+    , bfScale :: !Float       -- ^ The scale factor to fit the data into 8 bits.
+    } deriving (Eq, Show)
+
+-- | Four bitfields (red, green, blue, alpha)
+data Bitfields4 t = Bitfields4 !(Bitfield t)
+                               !(Bitfield t)
+                               !(Bitfield t)
+                               !(Bitfield t)
+                               deriving (Eq, Show)
+
+-- | Default bitfields 32 bit bitmaps.
+defaultBitfieldsRGB32 :: Bitfields3 Word32
+defaultBitfieldsRGB32 = Bitfields3 (makeBitfield 0x00FF0000)
+                                   (makeBitfield 0x0000FF00)
+                                   (makeBitfield 0x000000FF)
+
+-- | Default bitfields for 16 bit bitmaps.
+defaultBitfieldsRGB16 :: Bitfields3 Word16
+defaultBitfieldsRGB16 = Bitfields3 (makeBitfield 0x7C00)
+                                   (makeBitfield 0x03E0)
+                                   (makeBitfield 0x001F)
+
+-- | Three bitfields (red, gree, blue).
+data Bitfields3 t = Bitfields3 !(Bitfield t)
+                               !(Bitfield t)
+                               !(Bitfield t)
+                               deriving (Eq, Show)
+
+-- | Pixel formats used to encode RGBA image data.
+data RGBABmpFormat = RGBA32 !(Bitfields4 Word32)
+                   | RGBA16 !(Bitfields4 Word16)
+                   deriving (Eq, Show)
+
+-- | Pixel formats used to encode RGB image data.
+data RGBBmpFormat = RGB32 !(Bitfields3 Word32)
+                  | RGB24
+                  | RGB16 !(Bitfields3 Word16)
+                  deriving (Eq, Show)
+
+-- | Pixel formats used to encode indexed or grayscale images.
+data IndexedBmpFormat = OneBPP | FourBPP | EightBPP deriving Show
+
+-- | Extract pixel data from a bitfield.
+extractBitfield :: (FiniteBits t, Integral t) => Bitfield t -> t -> Word8
+extractBitfield bf t = if bfScale bf == 1
+                        then fromIntegral field
+                        else round $ bfScale bf * fromIntegral field
+  where field = (t .&. bfMask bf) `unsafeShiftR` bfShift bf
+
+-- | Convert a bit mask into a 'BitField'.
+makeBitfield :: (FiniteBits t, Integral t) => t -> Bitfield t
+makeBitfield mask = Bitfield mask shiftBits scale
+  where
+    shiftBits = countTrailingZeros mask
+    scale = 255 / fromIntegral (mask `unsafeShiftR` shiftBits)
+
+-- | Helper method to cast a 'B.ByteString' to a 'VS.Vector' of some type.
+castByteString :: VS.Storable a => B.ByteString -> VS.Vector a
+castByteString (BI.PS fp offset len) = VS.unsafeCast $ VS.unsafeFromForeignPtr fp offset len
+
+decodeImageRGBA8 :: RGBABmpFormat -> BmpV5Header -> B.ByteString -> Image PixelRGBA8
+decodeImageRGBA8 pixelFormat (BmpV5Header { width = w, height = h, bitPerPixel = bpp }) str = Image wi hi stArray where
   wi = fromIntegral w
   hi = abs $ fromIntegral h
   stArray = runST $ do
@@ -267,24 +531,38 @@         foldM_ (readLine arr) 0 [hi - 1, hi - 2 .. 0]
       VS.unsafeFreeze arr
 
-  stride = linePadding 32 wi -- will be 0
+  paddingWords = (8 * linePadding intBPP wi) `div` intBPP
+  intBPP = fromIntegral bpp
 
   readLine :: forall s. M.MVector s Word8 -> Int -> Int -> ST s Int
-  readLine arr readIndex line = inner readIndex writeIndex where
-    lastIndex = wi * (hi - 1 - line + 1) * 4
-    writeIndex = wi * (hi - 1 - line) * 4
+  readLine arr readIndex line = case pixelFormat of
+      RGBA32 bitfields -> inner bitfields (castByteString str) readIndex writeIndex
+      RGBA16 bitfields -> inner bitfields (castByteString str) readIndex writeIndex
+    where
+      lastIndex = wi * (hi - 1 - line + 1) * 4
+      writeIndex = wi * (hi - 1 - line) * 4
 
-    inner readIdx writeIdx | writeIdx >= lastIndex = return $ readIdx + stride
-    inner readIdx writeIdx = do
-        -- 32-bit BMP pixels are BGRA
-        (arr `M.unsafeWrite`  writeIdx     ) (str `B.index` (readIdx + posR))
-        (arr `M.unsafeWrite` (writeIdx + 1)) (str `B.index` (readIdx + posG))
-        (arr `M.unsafeWrite` (writeIdx + 2)) (str `B.index` (readIdx + posB))
-        (arr `M.unsafeWrite` (writeIdx + 3)) (str `B.index` (readIdx + posA))
-        inner (readIdx + 4) (writeIdx + 4)
+      inner
+        :: (FiniteBits t, Integral t, M.Storable t, Show t)
+        => Bitfields4 t
+        -> VS.Vector t
+        -> Int
+        -> Int
+        -> ST s Int
+      inner (Bitfields4 r g b a) inStr = inner0
+        where
+          inner0 :: Int -> Int -> ST s Int
+          inner0 readIdx writeIdx | writeIdx >= lastIndex = return $ readIdx + paddingWords
+          inner0 readIdx writeIdx = do
+            let word = inStr VS.! readIdx
+            (arr `M.unsafeWrite`  writeIdx     ) (extractBitfield r word)
+            (arr `M.unsafeWrite` (writeIdx + 1)) (extractBitfield g word)
+            (arr `M.unsafeWrite` (writeIdx + 2)) (extractBitfield b word)
+            (arr `M.unsafeWrite` (writeIdx + 3)) (extractBitfield a word)
+            inner0 (readIdx + 1) (writeIdx + 4)
 
-decodeImageRGB8 :: BmpInfoHeader -> B.ByteString -> Image PixelRGB8
-decodeImageRGB8 (BmpInfoHeader { width = w, height = h }) str = Image wi hi stArray where
+decodeImageRGB8 :: RGBBmpFormat -> BmpV5Header -> B.ByteString -> Image PixelRGB8
+decodeImageRGB8 pixelFormat (BmpV5Header { width = w, height = h, bitPerPixel = bpp }) str = Image wi hi stArray where
   wi = fromIntegral w
   hi = abs $ fromIntegral h
   stArray = runST $ do
@@ -295,22 +573,46 @@         foldM_ (readLine arr) 0 [hi - 1, hi - 2 .. 0]
       VS.unsafeFreeze arr
 
-  stride = linePadding 24 wi
+  paddingBytes = linePadding intBPP wi
+  paddingWords = (linePadding intBPP wi * 8) `div` intBPP
+  intBPP = fromIntegral bpp
 
   readLine :: forall s. M.MVector s Word8 -> Int -> Int -> ST s Int
-  readLine arr readIndex line = inner readIndex writeIndex where
-    lastIndex = wi * (hi - 1 - line + 1) * 3
-    writeIndex = wi * (hi - 1 - line) * 3
+  readLine arr readIndex line = case pixelFormat of
+      RGB16 bitfields -> innerBF bitfields (castByteString str) readIndex writeIndex
+      RGB32 bitfields -> innerBF bitfields (castByteString str) readIndex writeIndex
+      RGB24 -> inner24 readIndex writeIndex
+    where
+      lastIndex = wi * (hi - 1 - line + 1) * 3
+      writeIndex = wi * (hi - 1 - line) * 3
 
-    inner readIdx writeIdx | writeIdx >= lastIndex = return $ readIdx + stride
-    inner readIdx writeIdx = do
-        (arr `M.unsafeWrite`  writeIdx     ) (str `B.index` (readIdx + 2))
-        (arr `M.unsafeWrite` (writeIdx + 1)) (str `B.index` (readIdx + 1))
-        (arr `M.unsafeWrite` (writeIdx + 2)) (str `B.index`  readIdx)
-        inner (readIdx + 3) (writeIdx + 3)
+      inner24 readIdx writeIdx | writeIdx >= lastIndex = return $ readIdx + paddingBytes
+      inner24 readIdx writeIdx = do
+          (arr `M.unsafeWrite`  writeIdx     ) (str `B.index` (readIdx + 2))
+          (arr `M.unsafeWrite` (writeIdx + 1)) (str `B.index` (readIdx + 1))
+          (arr `M.unsafeWrite` (writeIdx + 2)) (str `B.index`  readIdx)
+          inner24 (readIdx + 3) (writeIdx + 3)
 
-decodeImageY8 :: BmpInfoHeader -> B.ByteString -> Image Pixel8
-decodeImageY8 (BmpInfoHeader { width = w, height = h }) str = Image wi hi stArray where
+      innerBF
+        :: (FiniteBits t, Integral t, M.Storable t, Show t)
+        => Bitfields3 t
+        -> VS.Vector t
+        -> Int
+        -> Int
+        -> ST s Int
+      innerBF (Bitfields3 r g b) inStr = innerBF0
+        where
+          innerBF0 :: Int -> Int -> ST s Int
+          innerBF0 readIdx writeIdx | writeIdx >= lastIndex = return $ readIdx + paddingWords
+          innerBF0 readIdx writeIdx = do
+            let word = inStr VS.! readIdx
+            (arr `M.unsafeWrite`  writeIdx     ) (extractBitfield r word)
+            (arr `M.unsafeWrite` (writeIdx + 1)) (extractBitfield g word)
+            (arr `M.unsafeWrite` (writeIdx + 2)) (extractBitfield b word)
+            innerBF0 (readIdx + 1) (writeIdx + 3)
+
+decodeImageY8 :: IndexedBmpFormat -> BmpV5Header -> B.ByteString -> Image Pixel8
+decodeImageY8 lowBPP (BmpV5Header { width = w, height = h, bitPerPixel = bpp }) str = Image wi hi stArray where
   wi = fromIntegral w
   hi = abs $ fromIntegral h
   stArray = runST $ do
@@ -321,33 +623,131 @@         foldM_ (readLine arr) 0 [hi - 1, hi - 2 .. 0]
       VS.unsafeFreeze arr
 
-  stride = linePadding 8 wi
+  padding = linePadding (fromIntegral bpp) wi
   
   readLine :: forall s. M.MVector s Word8 -> Int -> Int -> ST s Int
-  readLine arr readIndex line = inner readIndex writeIndex where
-    lastIndex = wi * (hi - 1 - line + 1)
-    writeIndex = wi * (hi - 1 - line)
+  readLine arr readIndex line = case lowBPP of
+      OneBPP -> inner1 readIndex writeIndex
+      FourBPP -> inner4 readIndex writeIndex
+      EightBPP -> inner8 readIndex writeIndex
+    where
+      lastIndex = wi * (hi - 1 - line + 1)
+      writeIndex = wi * (hi - 1 - line)
 
-    inner readIdx writeIdx | writeIdx >= lastIndex = return $ readIdx + stride
-    inner readIdx writeIdx = do
-      (arr `M.unsafeWrite` writeIdx) (str `B.index` readIdx)
-      inner (readIdx + 1) (writeIdx + 1)
+      inner8 readIdx writeIdx | writeIdx >= lastIndex = return $ readIdx + padding
+      inner8 readIdx writeIdx = do
+        (arr `M.unsafeWrite` writeIdx) (str `B.index` readIdx)
+        inner8 (readIdx + 1) (writeIdx + 1)
 
+      inner4 readIdx writeIdx | writeIdx >= lastIndex = return $ readIdx + padding
+      inner4 readIdx writeIdx = do
+        let byte = str `B.index` readIdx
+        if writeIdx >= lastIndex - 1 then do
+          (arr `M.unsafeWrite` writeIdx) (byte `unsafeShiftR` 4)
+          inner4 (readIdx + 1) (writeIdx + 1)
+        else do
+          (arr `M.unsafeWrite` writeIdx) (byte `unsafeShiftR` 4)
+          (arr `M.unsafeWrite` (writeIdx + 1)) (byte .&. 0x0F)
+          inner4 (readIdx + 1) (writeIdx + 2)
 
-pixelGet :: Get [Word8]
-pixelGet = do
+      inner1 readIdx writeIdx | writeIdx >= lastIndex = return $ readIdx + padding
+      inner1 readIdx writeIdx = do
+        let byte = str `B.index` readIdx
+        let toWrite = (lastIndex - writeIdx) `min` 8
+        forM_ [0 .. (toWrite - 1)] $ \i ->
+          when (byte `testBit` (7 - i)) $ (arr `M.unsafeWrite` (writeIdx + i)) 1
+        inner1 (readIdx + 1) (writeIdx + toWrite)
+
+decodeImageY8RLE :: Bool -> BmpV5Header -> B.ByteString -> Image Pixel8
+decodeImageY8RLE is4bpp (BmpV5Header { width = w, height = h, byteImageSize = sz }) str = Image wi hi stArray where
+  wi = fromIntegral w
+  hi = abs $ fromIntegral h
+  xOffsetMax = wi - 1
+
+  stArray = runST $ do
+    arr <- M.new . fromIntegral $ w * abs h
+    decodeRLE arr (B.unpack (B.take (fromIntegral sz) str)) ((hi - 1) * wi, 0)
+    VS.unsafeFreeze arr
+
+  decodeRLE :: forall s . M.MVector s Word8 -> [Word8] -> (Int, Int) -> ST s ()
+  decodeRLE arr = inner
+    where
+      inner :: [Word8] -> (Int, Int) -> ST s ()
+      inner [] _ = return ()
+      inner (0 : 0 : rest) (yOffset, _) = inner rest (yOffset - wi, 0)
+      inner (0 : 1 : _) _ = return ()
+      inner (0 : 2 : hOffset : vOffset : rest) (yOffset, _) =
+        inner rest (yOffset - (wi * fromIntegral vOffset), fromIntegral hOffset)
+      inner (0 : n : rest) writePos = 
+        let isPadded = if is4bpp then (n + 3) .&. 0x3 < 2 else odd n
+        in copyN isPadded (fromIntegral n) rest writePos
+      inner (n : b : rest) writePos = writeN (fromIntegral n) b rest writePos
+      inner _ _ = return ()
+
+      -- | Write n copies of a byte to the output array.
+      writeN :: Int -> Word8 -> [Word8] -> (Int, Int) -> ST s ()
+      writeN 0 _ rest writePos = inner rest writePos
+      writeN n b rest writePos =
+        case (is4bpp, n) of
+          (True, 1) ->
+            writeByte (b `unsafeShiftR` 4) writePos >>= writeN (n - 1) b rest
+          (True, _) ->
+            writeByte (b `unsafeShiftR` 4) writePos
+              >>= writeByte (b .&. 0x0F) >>= writeN (n - 2) b rest
+          (False, _) ->
+            writeByte b writePos >>= writeN (n - 1) b rest
+
+      -- | Copy the next byte to the output array, possibly ignoring a padding byte at the end.
+      copyN :: Bool -> Int -> [Word8] -> (Int, Int) -> ST s ()
+      copyN _ _ [] _ = return ()
+      copyN False 0 rest writePos = inner rest writePos
+      copyN True 0 (_:rest) writePos = inner rest writePos
+      copyN isPadded n (b : rest) writePos =
+        case (is4bpp, n) of
+          (True, 1) ->
+            writeByte (b `unsafeShiftR` 4) writePos >>= copyN isPadded (n - 1) rest
+          (True, _) ->
+            writeByte (b `unsafeShiftR` 4) writePos
+              >>= writeByte (b .&. 0x0F) >>= copyN isPadded (n - 2) rest
+          (False, _) ->
+            writeByte b writePos >>= copyN isPadded (n - 1) rest
+
+      -- | Write the next byte to the output array.
+      writeByte :: Word8 -> (Int, Int) -> ST s (Int, Int)
+      writeByte byte (yOffset, xOffset) = do
+        (arr `M.unsafeWrite` (yOffset + xOffset)) byte
+        return (yOffset, (xOffset + 1) `min` xOffsetMax)
+
+pixel4Get :: Get [Word8]
+pixel4Get = do
     b <- getWord8
     g <- getWord8
     r <- getWord8
     _ <- getWord8
-    return $ [r, g, b]
+    return [r, g, b]
 
-metadataOfHeader :: BmpInfoHeader -> Metadatas
-metadataOfHeader hdr = 
-  Met.simpleMetadata Met.SourceBitmap (width hdr) (abs $ height hdr) dpiX dpiY
+pixel3Get :: Get [Word8]
+pixel3Get = do
+    b <- getWord8
+    g <- getWord8
+    r <- getWord8
+    return [r, g, b]
+
+metadataOfHeader :: BmpV5Header -> Maybe B.ByteString -> Metadatas
+metadataOfHeader hdr iccProfile =
+    cs <> Met.simpleMetadata Met.SourceBitmap (width hdr) (abs $ height hdr) dpiX dpiY
   where
     dpiX = Met.dotsPerMeterToDotPerInch . fromIntegral $ xResolution hdr
     dpiY = Met.dotsPerMeterToDotPerInch . fromIntegral $ yResolution hdr
+    cs = case colorSpaceType hdr of
+          CalibratedRGB -> Met.singleton
+            Met.ColorSpace (Met.WindowsBitmapColorSpace $ colorSpace hdr)
+          SRGB -> Met.singleton Met.ColorSpace Met.SRGB
+          ProfileEmbedded -> case iccProfile of
+                              Nothing -> Met.empty
+                              Just profile -> Met.singleton Met.ColorSpace
+                                                (Met.ICCProfile profile)
+          _ -> Met.empty
 
 -- | Try to decode a bitmap image.
 -- Right now this function can output the following image:
@@ -369,71 +769,130 @@ -- | Same as 'decodeBitmap' but also extracts metadata and provide separated palette.
 decodeBitmapWithPaletteAndMetadata :: B.ByteString -> Either String (PalettedImage, Metadatas)
 decodeBitmapWithPaletteAndMetadata str = flip runGetStrict str $ do
-  hdr      <- get :: Get BmpHeader
-  bmpHeader <- get :: Get BmpInfoHeader
+  fileHeader <- get :: Get BmpHeader
+  bmpHeader  <- get :: Get BmpV5Header
 
   readed <- bytesRead
-  when (readed > fromIntegral (dataOffset hdr))
+  when (readed > fromIntegral (dataOffset fileHeader))
        (fail "Invalid bmp image, data in header")
-  
+
   when (width bmpHeader <= 0)
        (fail $ "Invalid bmp width, " ++ show (width bmpHeader))
 
   when (height bmpHeader == 0)
        (fail $ "Invalid bmp height (0) ")
 
-  let bpp = fromIntegral $ bitPerPixel bmpHeader :: Int
-      paletteColorCount
-        | colorCount bmpHeader == 0 = 2 ^ bpp
-        | otherwise = fromIntegral $ colorCount bmpHeader
-      getData = do
-        readed' <- bytesRead
-        skip . fromIntegral $ dataOffset hdr - fromIntegral readed'
-        getRemainingBytes
-      addMetadata i = (i, metadataOfHeader bmpHeader)
+  decodeBitmapWithHeaders fileHeader bmpHeader
 
-  case (bitPerPixel bmpHeader, planes  bmpHeader,
-              bitmapCompression bmpHeader) of
-    (32, 1, 0) -> do
-      rest <- getData
-      return . addMetadata . TrueColorImage . ImageRGBA8 
-             $ decodeImageRGBA8 bmpHeader (2, 1, 0, 3) rest
-      -- (2, 1, 0, 3) means BGRA pixel order
-    (32, 1, 3) -> do
-      posRed   <- getBitfield
-      posGreen <- getBitfield
-      posBlue  <- getBitfield
-      posAlpha <- getBitfield
-      rest     <- getData
-      return . addMetadata . TrueColorImage . ImageRGBA8 $
-        decodeImageRGBA8 bmpHeader (posRed, posGreen, posBlue, posAlpha) rest
-    (24, 1, 0) -> do
-      rest <- getData
-      return . addMetadata . TrueColorImage . ImageRGB8  $ 
-        decodeImageRGB8  bmpHeader rest
-    ( 8, 1, 0) -> do
-      table <- replicateM paletteColorCount pixelGet
-      rest <- getData
-      let palette = Palette'
-            { _paletteSize = paletteColorCount
-            , _paletteData = VS.fromListN (paletteColorCount * 3) $ concat table
-            }
-      return . addMetadata $ PalettedRGB8 (decodeImageY8 bmpHeader rest) palette
+-- | Decode the rest of a bitmap, after the headers have been decoded.
+decodeBitmapWithHeaders :: BmpHeader -> BmpV5Header -> Get (PalettedImage, Metadatas)
+decodeBitmapWithHeaders fileHdr hdr = do
+    img <- bitmapData
+    profile <- getICCProfile
+    return $ addMetadata profile img
 
-    a          -> fail $ "Can't handle BMP file " ++ show a
+  where
+    bpp = fromIntegral $ bitPerPixel hdr :: Int
+    paletteColorCount
+      | colorCount hdr == 0 = 2 ^ bpp
+      | otherwise = fromIntegral $ colorCount hdr
 
+    addMetadata profile i = (i, metadataOfHeader hdr profile)
 
-getBitfield :: Get Int
-getBitfield = do
-  w32 <- getWord32be
-  case w32 of
-    0xFF000000 -> return 0
-    0x00FF0000 -> return 1
-    0x0000FF00 -> return 2
-    0x000000FF -> return 3
-    _          -> fail $
-      "Codec.Picture.Bitmap.getBitfield: unsupported bitfield of " ++ show w32
+    getData = do
+      readed <- bytesRead
+      label "Start of pixel data" $
+        skip . fromIntegral $ dataOffset fileHdr - fromIntegral readed
+      let pixelBytes = if bitmapCompression hdr == 1 || bitmapCompression hdr == 2
+                          then fromIntegral $ byteImageSize hdr
+                          else sizeofPixelData bpp (fromIntegral $ width hdr)
+                                                   (fromIntegral $ height hdr)
+      label "Pixel data" $ getByteString pixelBytes
 
+    getICCProfile =
+      if size hdr >= sizeofBmpV5Header
+          && colorSpaceType hdr == ProfileLinked
+          && iccProfileData hdr > 0
+          && iccProfileSize hdr > 0
+      then do
+        readSoFar <- bytesRead
+        label "Start of embedded ICC color profile" $
+          skip $ fromIntegral (iccProfileData hdr) - fromIntegral readSoFar
+        profile <- label "Embedded ICC color profile" $
+                      getByteString . fromIntegral $ iccProfileSize hdr
+        return (Just profile)
+      else return Nothing
+
+    bitmapData = case (bitPerPixel hdr, planes hdr, bitmapCompression hdr) of
+      (32, 1, 0) -> do
+        rest <- getData
+        return . TrueColorImage . ImageRGB8 $
+          decodeImageRGB8 (RGB32 defaultBitfieldsRGB32) hdr rest
+        -- (2, 1, 0, 3) means BGRA pixel order
+      (32, 1, 3) -> do
+        r <- getBitfield $ redMask hdr
+        g <- getBitfield $ greenMask hdr
+        b <- getBitfield $ blueMask hdr
+        rest     <- getData
+        if alphaMask hdr == 0
+          then return . TrueColorImage . ImageRGB8 $
+            decodeImageRGB8 (RGB32 $ Bitfields3 r g b) hdr rest
+          else do
+            a <- getBitfield $ alphaMask hdr
+            return . TrueColorImage . ImageRGBA8 $
+              decodeImageRGBA8 (RGBA32 $ Bitfields4 r g b a) hdr rest
+      (24, 1, 0) -> do
+        rest <- getData
+        return . TrueColorImage . ImageRGB8 $
+          decodeImageRGB8 RGB24 hdr rest
+      (16, 1, 0) -> do
+        rest <- getData
+        return . TrueColorImage . ImageRGB8 $ 
+          decodeImageRGB8 (RGB16 defaultBitfieldsRGB16) hdr rest
+      (16, 1, 3) -> do
+        r <- getBitfield . fromIntegral $ 0xFFFF .&. redMask hdr
+        g <- getBitfield . fromIntegral $ 0xFFFF .&. greenMask hdr
+        b <- getBitfield . fromIntegral $ 0xFFFF .&. blueMask hdr
+        rest     <- getData
+        if alphaMask hdr == 0
+          then return . TrueColorImage . ImageRGB8 $
+            decodeImageRGB8 (RGB16 $ Bitfields3 r g b) hdr rest
+          else do
+            a <- getBitfield . fromIntegral $ 0xFFFF .&. alphaMask hdr
+            return . TrueColorImage . ImageRGBA8 $
+              decodeImageRGBA8 (RGBA16 $ Bitfields4 r g b a) hdr rest
+      ( _, 1, compression) -> do
+        table <- if size hdr == sizeofBmpCoreHeader
+                    then replicateM paletteColorCount pixel3Get
+                    else replicateM paletteColorCount pixel4Get
+        rest <- getData
+        let palette = Palette'
+              { _paletteSize = paletteColorCount
+              , _paletteData = VS.fromListN (paletteColorCount * 3) $ concat table
+              }
+        image <-
+          case (bpp, compression) of
+            (8, 0) -> return $ decodeImageY8 EightBPP hdr rest
+            (4, 0) -> return $ decodeImageY8 FourBPP hdr rest
+            (1, 0) -> return $ decodeImageY8 OneBPP hdr rest
+            (8, 1) -> return $ decodeImageY8RLE False hdr rest
+            (4, 2) -> return $ decodeImageY8RLE True hdr rest
+            (a, b) -> fail $ "Can't handle BMP file " ++ show (a, 1 :: Int, b)
+
+        return $ PalettedRGB8 image palette
+
+      a          -> fail $ "Can't handle BMP file " ++ show a
+
+-- | Decode a bitfield. Will fail if the bitfield is empty.
+getBitfield :: (FiniteBits t, Integral t, Num t, Monad m) => t -> m (Bitfield t)
+getBitfield 0 = fail $
+  "Codec.Picture.Bitmap.getBitfield: bitfield cannot be 0"
+getBitfield w = return (makeBitfield w)
+
+-- | Compute the size of the pixel data
+sizeofPixelData :: Int -> Int -> Int -> Int
+sizeofPixelData bpp lineWidth nLines = ((bpp * (abs lineWidth) + 31) `div` 32) * 4 * abs nLines
+
 -- | Write an image in a file use the bitmap format.
 writeBitmap :: (BmpEncodable pixel)
             => FilePath -> Image pixel -> IO ()
@@ -441,7 +900,7 @@ 
 linePadding :: Int -> Int -> Int
 linePadding bpp imgWidth = (4 - (bytesPerLine `mod` 4)) `mod` 4
-    where bytesPerLine = imgWidth * (fromIntegral bpp `div` 8)
+  where bytesPerLine = (bpp * imgWidth + 7) `div` 8
 
 -- | Encode an image into a bytestring in .bmp format ready to be written
 -- on disk.
@@ -482,8 +941,7 @@ 
 extractDpiOfMetadata :: Metadatas -> (Word32, Word32)
 extractDpiOfMetadata metas = (fetch Met.DpiX, fetch Met.DpiY) where
-  fetch k = fromMaybe 0
-          $ fromIntegral . Met.dotPerInchToDotsPerMeter <$> Met.lookup k metas
+  fetch k = maybe 0 (fromIntegral . Met.dotPerInchToDotsPerMeter) $ Met.lookup k metas
 
 -- | Convert an image to a bytestring ready to be serialized.
 encodeBitmapWithPalette :: forall pixel. (BmpEncodable pixel)
@@ -501,34 +959,69 @@                                    -> L.ByteString
 encodeBitmapWithPaletteAndMetadata metas pal@(BmpPalette palette) img =
   runPut $ put hdr >> put info >> putPalette pal >> bmpEncode img
+                   >> putICCProfile colorProfileData
+
     where imgWidth = fromIntegral $ imageWidth img
           imgHeight = fromIntegral $ imageHeight img
           (dpiX, dpiY) = extractDpiOfMetadata metas
+          cs = Met.lookup Met.ColorSpace metas
+          colorType = case cs of
+                        Just Met.SRGB -> SRGB
+                        Just (Met.WindowsBitmapColorSpace _) -> CalibratedRGB
+                        Just (Met.ICCProfile _) -> ProfileEmbedded
+                        Nothing -> DeviceDependentRGB
 
+          colorSpaceInfo = case cs of
+                            Just (Met.WindowsBitmapColorSpace bytes) -> bytes
+                            _ -> B.pack $ replicate sizeofColorProfile 0
+
+          colorProfileData = case cs of
+                              Just (Met.ICCProfile bytes) -> Just bytes
+                              _ -> Nothing
+
+          headerSize | colorType == ProfileEmbedded                = sizeofBmpV5Header
+                     | colorType == CalibratedRGB || hasAlpha img  = sizeofBmpV4Header
+                     | otherwise                                   = sizeofBmpInfoHeader
+
           paletteSize = fromIntegral $ length palette
           bpp = bitsPerPixel (undefined :: pixel)
-          padding = linePadding bpp imgWidth
-          imagePixelSize = fromIntegral $ (imgWidth * div bpp 8 + padding) * imgHeight
+
+          profileSize = fromIntegral $ maybe 0 B.length colorProfileData
+          imagePixelSize = fromIntegral $ sizeofPixelData bpp imgWidth imgHeight
+          offsetToData = sizeofBmpHeader + headerSize + 4 * paletteSize
+          offsetToICCProfile = offsetToData + imagePixelSize <$ colorProfileData
+          sizeOfFile = sizeofBmpHeader + headerSize + 4 * paletteSize
+                        + imagePixelSize + profileSize
+
           hdr = BmpHeader {
               magicIdentifier = bitmapMagicIdentifier,
-              fileSize = sizeofBmpHeader + sizeofBmpInfo + 4 * paletteSize + imagePixelSize,
+              fileSize = sizeOfFile,
               reserved1 = 0,
               reserved2 = 0,
-              dataOffset = sizeofBmpHeader + sizeofBmpInfo + 4 * paletteSize
+              dataOffset = offsetToData
           }
 
-          info = BmpInfoHeader {
-              size = sizeofBmpInfo,
+          info = BmpV5Header {
+              size = headerSize,
               width = fromIntegral imgWidth,
               height = fromIntegral imgHeight,
               planes = 1,
               bitPerPixel = fromIntegral bpp,
-              bitmapCompression = 0, -- no compression
+              bitmapCompression = if hasAlpha img then 3 else 0,
               byteImageSize = imagePixelSize,
               xResolution = fromIntegral dpiX,
               yResolution = fromIntegral dpiY,
-              colorCount = 0,
-              importantColours = paletteSize
+              colorCount = paletteSize,
+              importantColours = 0,
+              redMask   = if hasAlpha img then 0x00FF0000 else 0,
+              greenMask = if hasAlpha img then 0x0000FF00 else 0,
+              blueMask  = if hasAlpha img then 0x000000FF else 0,
+              alphaMask = if hasAlpha img then 0xFF000000 else 0,
+              colorSpaceType = colorType,
+              colorSpace = colorSpaceInfo,
+              iccIntent = 0,
+              iccProfileData = fromMaybe 0 offsetToICCProfile,
+              iccProfileSize = profileSize
           }
 
 
src/Codec/Picture/Gif.hs view
@@ -69,8 +69,8 @@ import Codec.Picture.Metadata( Metadatas
                              , SourceFormat( SourceGif )
                              , basicMetadata )
-import Codec.Picture.Gif.LZW
-import Codec.Picture.Gif.LZWEncoding
+import Codec.Picture.Gif.Internal.LZW
+import Codec.Picture.Gif.Internal.LZWEncoding
 import Codec.Picture.BitWriter
 
 -- | Delay to wait before showing the next Gif image.
+ src/Codec/Picture/Gif/Internal/LZW.hs view
@@ -0,0 +1,194 @@+{-# LANGUAGE CPP #-}
+module Codec.Picture.Gif.Internal.LZW( decodeLzw, decodeLzwTiff ) where
+
+#if !MIN_VERSION_base(4,8,0)
+import Control.Applicative( (<$>) )
+#endif
+
+import Data.Word( Word8 )
+import Control.Monad( when, unless )
+
+import Data.Bits( (.&.) )
+
+import Control.Monad.ST( ST )
+import Control.Monad.Trans.Class( MonadTrans, lift )
+
+import Foreign.Storable ( Storable )
+
+import qualified Data.ByteString as B
+import qualified Data.Vector.Storable.Mutable as M
+
+import Codec.Picture.BitWriter
+
+{-# INLINE (.!!!.) #-}
+(.!!!.) :: (Storable a) => M.STVector s a -> Int -> ST s a
+(.!!!.) = M.unsafeRead 
+        {-M.read-}
+
+{-# INLINE (..!!!..) #-}
+(..!!!..) :: (MonadTrans t, Storable a)
+          => M.STVector s a -> Int -> t (ST s) a
+(..!!!..) v idx = lift $ v .!!!. idx
+
+{-# INLINE (.<-.) #-}
+(.<-.) :: (Storable a) => M.STVector s a -> Int -> a -> ST s ()
+(.<-.) = M.unsafeWrite 
+         {-M.write-}
+
+{-# INLINE (..<-..) #-}
+(..<-..) :: (MonadTrans t, Storable a)
+         => M.STVector s a -> Int -> a -> t (ST s) ()
+(..<-..) v idx = lift . (v .<-. idx)
+
+
+duplicateData :: (MonadTrans t, Storable a)
+              => M.STVector s a -> M.STVector s a
+              -> Int -> Int -> Int -> t (ST s) ()
+duplicateData src dest sourceIndex size destIndex = lift $ aux sourceIndex destIndex
+  where endIndex = sourceIndex + size
+        aux i _ | i == endIndex  = return ()
+        aux i j = do
+          src .!!!. i >>= (dest .<-. j)
+          aux (i + 1) (j + 1)
+
+rangeSetter :: (Storable a, Num a)
+            => Int -> M.STVector s a
+            -> ST s (M.STVector s a)
+rangeSetter count vec = aux 0
+  where aux n | n == count = return vec
+        aux n = (vec .<-. n) (fromIntegral n) >> aux (n + 1)
+
+decodeLzw :: B.ByteString -> Int -> Int -> M.STVector s Word8
+          -> BoolReader s ()
+decodeLzw str maxBitKey initialKey outVec = do
+    setDecodedString str
+    lzw GifVariant maxBitKey initialKey 0 outVec
+
+isOldTiffLZW :: B.ByteString -> Bool
+isOldTiffLZW str = firstByte == 0 && secondByte == 1
+    where firstByte = str `B.index` 0
+          secondByte = (str `B.index` 1) .&. 1
+
+decodeLzwTiff :: B.ByteString -> M.STVector s Word8 -> Int
+              -> BoolReader s()
+decodeLzwTiff str outVec initialWriteIdx = do
+    if isOldTiffLZW str then
+      setDecodedString str
+    else
+      setDecodedStringMSB str
+    let variant | isOldTiffLZW str = OldTiffVariant
+                | otherwise = TiffVariant
+    lzw variant 12 9 initialWriteIdx outVec
+
+data TiffVariant =
+      GifVariant
+    | TiffVariant
+    | OldTiffVariant
+    deriving Eq
+
+-- | Gif image constraint from spec-gif89a, code size max : 12 bits.
+lzw :: TiffVariant -> Int -> Int -> Int -> M.STVector s Word8
+    -> BoolReader s ()
+lzw variant nMaxBitKeySize initialKeySize initialWriteIdx outVec = do
+    -- Allocate buffer of maximum size.
+    lzwData <- lift (M.replicate maxDataSize 0) >>= resetArray
+    lzwOffsetTable <- lift (M.replicate tableEntryCount 0) >>= resetArray
+    lzwSizeTable <- lift $ M.replicate tableEntryCount 0
+    lift $ lzwSizeTable `M.set` 1
+
+    let firstVal code = do
+            dataOffset <- lzwOffsetTable ..!!!.. code
+            lzwData ..!!!.. dataOffset
+
+        writeString at code = do
+            dataOffset <- lzwOffsetTable ..!!!.. code
+            dataSize   <- lzwSizeTable   ..!!!.. code
+
+            when (at + dataSize <= maxWrite) $
+                 duplicateData lzwData outVec dataOffset dataSize at
+
+            return dataSize
+
+        addString pos at code val = do
+            dataOffset <- lzwOffsetTable ..!!!.. code
+            dataSize   <- lzwSizeTable   ..!!!.. code
+
+            when (pos < tableEntryCount) $ do
+              (lzwOffsetTable ..<-.. pos) at
+              (lzwSizeTable ..<-.. pos) $ dataSize + 1
+
+            when (at + dataSize + 1 <= maxDataSize) $ do
+              duplicateData lzwData lzwData dataOffset dataSize at
+              (lzwData ..<-.. (at + dataSize)) val
+
+            return $ dataSize + 1
+
+        maxWrite = M.length outVec
+        loop outWriteIdx writeIdx dicWriteIdx codeSize oldCode code
+          | outWriteIdx >= maxWrite = return ()
+          | code == endOfInfo = return ()
+          | code == clearCode = do
+              toOutput <- getNextCode startCodeSize
+              unless (toOutput == endOfInfo) $ do
+                dataSize <- writeString outWriteIdx toOutput
+                getNextCode startCodeSize >>=
+                  loop (outWriteIdx + dataSize)
+                       firstFreeIndex firstFreeIndex startCodeSize toOutput
+
+          | otherwise =  do
+              (written, dicAdd) <-
+                   if code >= writeIdx then do
+                     c <- firstVal oldCode
+                     wroteSize <- writeString outWriteIdx oldCode
+                     (outVec ..<-.. (outWriteIdx + wroteSize)) c
+                     addedSize <- addString writeIdx dicWriteIdx oldCode c
+                     return (wroteSize + 1, addedSize)
+                   else do
+                     wroteSize <- writeString outWriteIdx code
+                     c <- firstVal code
+                     addedSize <- addString writeIdx dicWriteIdx oldCode c
+                     return (wroteSize, addedSize)
+
+              let new_code_size = updateCodeSize codeSize $ writeIdx + 1
+              getNextCode new_code_size >>=
+                loop (outWriteIdx + written)
+                     (writeIdx + 1)
+                     (dicWriteIdx + dicAdd)
+                     new_code_size
+                     code
+
+    getNextCode startCodeSize >>=
+        loop initialWriteIdx firstFreeIndex firstFreeIndex startCodeSize 0
+
+  where tableEntryCount =  2 ^ min 12 nMaxBitKeySize
+        maxDataSize = tableEntryCount `div` 2 * (1 + tableEntryCount) + 1
+
+        isNewTiff = variant == TiffVariant
+        (switchOffset,  isTiffVariant) = case variant of
+            GifVariant -> (0, False)
+            TiffVariant -> (1, True)
+            OldTiffVariant -> (0, True)
+
+        initialElementCount = 2 ^ initialKeySize :: Int
+        clearCode | isTiffVariant = 256
+                  | otherwise = initialElementCount
+
+        endOfInfo | isTiffVariant = 257
+                  | otherwise = clearCode + 1
+
+        startCodeSize 
+                  | isTiffVariant = initialKeySize
+                  | otherwise = initialKeySize + 1
+
+        firstFreeIndex = endOfInfo + 1
+
+        resetArray a = lift $ rangeSetter initialElementCount a
+
+        updateCodeSize codeSize writeIdx
+            | writeIdx == 2 ^ codeSize - switchOffset = min 12 $ codeSize + 1
+            | otherwise = codeSize
+
+        getNextCode s 
+            | isNewTiff = fromIntegral <$> getNextBitsMSBFirst s
+            | otherwise = fromIntegral <$> getNextBitsLSBFirst s
+
+ src/Codec/Picture/Gif/Internal/LZWEncoding.hs view
@@ -0,0 +1,101 @@+{-# LANGUAGE BangPatterns, CPP #-}
+module Codec.Picture.Gif.Internal.LZWEncoding( lzwEncode ) where
+
+#if !MIN_VERSION_base(4,8,0)
+import Control.Applicative( (<$>) )
+import Data.Monoid( mempty )
+#endif
+
+import Control.Monad.ST( runST )
+import qualified Data.ByteString.Lazy as L
+import Data.Maybe( fromMaybe )
+import Data.Word( Word8 )
+
+#if MIN_VERSION_containers(0,5,0)
+import qualified Data.IntMap.Strict as I
+#else
+import qualified Data.IntMap as I
+#endif
+import qualified Data.Vector.Storable as V
+
+import Codec.Picture.BitWriter
+
+type Trie = I.IntMap TrieNode
+
+data TrieNode = TrieNode
+    { trieIndex :: {-# UNPACK #-} !Int
+    , trieSub   :: !Trie
+    }
+
+emptyNode :: TrieNode
+emptyNode = TrieNode
+    { trieIndex = -1
+    , trieSub = mempty
+    }
+
+initialTrie :: Trie
+initialTrie = I.fromList
+    [(i, emptyNode { trieIndex = i }) | i <- [0 .. 255]]
+
+lookupUpdate :: V.Vector Word8 -> Int -> Int -> Trie -> (Int, Int, Trie)
+lookupUpdate vector freeIndex firstIndex trie =
+    matchUpdate $ go trie 0 firstIndex 
+  where
+    matchUpdate (lzwOutputIndex, nextReadIndex, sub) =
+        (lzwOutputIndex, nextReadIndex, fromMaybe trie sub)
+
+    maxi = V.length vector
+    go !currentTrie !prevIndex !index
+      | index >= maxi = (prevIndex, index, Nothing)
+      | otherwise = case I.lookup val currentTrie of
+          Just (TrieNode ix subTable) ->
+              let (lzwOutputIndex, nextReadIndex, newTable) =
+                        go subTable ix $ index + 1
+                  tableUpdater t =
+                      I.insert val (TrieNode ix t) currentTrie
+              in
+              (lzwOutputIndex, nextReadIndex, tableUpdater <$> newTable)
+
+          Nothing | index == maxi -> (prevIndex, index, Nothing)
+                  | otherwise -> (prevIndex, index, Just $ I.insert val newNode currentTrie)
+
+      where val = fromIntegral $ vector `V.unsafeIndex` index
+            newNode = emptyNode { trieIndex = freeIndex }
+
+lzwEncode :: Int -> V.Vector Word8 -> L.ByteString
+lzwEncode initialKeySize vec = runST $ do
+    bitWriter <- newWriteStateRef 
+
+    let updateCodeSize 12 writeIdx _
+            | writeIdx == 2 ^ (12 :: Int) - 1 = do
+               writeBitsGif bitWriter (fromIntegral clearCode) 12
+               return (startCodeSize, firstFreeIndex, initialTrie)
+
+        updateCodeSize codeSize writeIdx trie
+            | writeIdx == 2 ^ codeSize =
+                return (codeSize + 1, writeIdx + 1, trie)
+            | otherwise = return (codeSize, writeIdx + 1, trie)
+
+        go readIndex (codeSize, _, _) | readIndex >= maxi =
+            writeBitsGif bitWriter (fromIntegral endOfInfo) codeSize
+        go !readIndex (!codeSize, !writeIndex, !trie) = do
+            let (indexToWrite, endIndex, trie') =
+                    lookuper writeIndex readIndex trie
+            writeBitsGif bitWriter (fromIntegral indexToWrite) codeSize
+            updateCodeSize codeSize writeIndex trie'
+                >>= go endIndex 
+
+    writeBitsGif bitWriter (fromIntegral clearCode) startCodeSize
+    go 0 (startCodeSize, firstFreeIndex, initialTrie)
+
+    finalizeBoolWriterGif bitWriter
+  where
+    maxi = V.length vec
+
+    startCodeSize = initialKeySize + 1
+
+    clearCode = 2 ^ initialKeySize :: Int
+    endOfInfo = clearCode + 1
+    firstFreeIndex = endOfInfo + 1
+    
+    lookuper = lookupUpdate vec
− src/Codec/Picture/Gif/LZW.hs
@@ -1,194 +0,0 @@-{-# LANGUAGE CPP #-}-module Codec.Picture.Gif.LZW( decodeLzw, decodeLzwTiff ) where--#if !MIN_VERSION_base(4,8,0)-import Control.Applicative( (<$>) )-#endif--import Data.Word( Word8 )-import Control.Monad( when, unless )--import Data.Bits( (.&.) )--import Control.Monad.ST( ST )-import Control.Monad.Trans.Class( MonadTrans, lift )--import Foreign.Storable ( Storable )--import qualified Data.ByteString as B-import qualified Data.Vector.Storable.Mutable as M--import Codec.Picture.BitWriter--{-# INLINE (.!!!.) #-}-(.!!!.) :: (Storable a) => M.STVector s a -> Int -> ST s a-(.!!!.) = M.unsafeRead -        {-M.read-}--{-# INLINE (..!!!..) #-}-(..!!!..) :: (MonadTrans t, Storable a)-          => M.STVector s a -> Int -> t (ST s) a-(..!!!..) v idx = lift $ v .!!!. idx--{-# INLINE (.<-.) #-}-(.<-.) :: (Storable a) => M.STVector s a -> Int -> a -> ST s ()-(.<-.) = M.unsafeWrite -         {-M.write-}--{-# INLINE (..<-..) #-}-(..<-..) :: (MonadTrans t, Storable a)-         => M.STVector s a -> Int -> a -> t (ST s) ()-(..<-..) v idx = lift . (v .<-. idx)---duplicateData :: (MonadTrans t, Storable a)-              => M.STVector s a -> M.STVector s a-              -> Int -> Int -> Int -> t (ST s) ()-duplicateData src dest sourceIndex size destIndex = lift $ aux sourceIndex destIndex-  where endIndex = sourceIndex + size-        aux i _ | i == endIndex  = return ()-        aux i j = do-          src .!!!. i >>= (dest .<-. j)-          aux (i + 1) (j + 1)--rangeSetter :: (Storable a, Num a)-            => Int -> M.STVector s a-            -> ST s (M.STVector s a)-rangeSetter count vec = aux 0-  where aux n | n == count = return vec-        aux n = (vec .<-. n) (fromIntegral n) >> aux (n + 1)--decodeLzw :: B.ByteString -> Int -> Int -> M.STVector s Word8-          -> BoolReader s ()-decodeLzw str maxBitKey initialKey outVec = do-    setDecodedString str-    lzw GifVariant maxBitKey initialKey 0 outVec--isOldTiffLZW :: B.ByteString -> Bool-isOldTiffLZW str = firstByte == 0 && secondByte == 1-    where firstByte = str `B.index` 0-          secondByte = (str `B.index` 1) .&. 1--decodeLzwTiff :: B.ByteString -> M.STVector s Word8 -> Int-              -> BoolReader s()-decodeLzwTiff str outVec initialWriteIdx = do-    if isOldTiffLZW str then-      setDecodedString str-    else-      setDecodedStringMSB str-    let variant | isOldTiffLZW str = OldTiffVariant-                | otherwise = TiffVariant-    lzw variant 12 9 initialWriteIdx outVec--data TiffVariant =-      GifVariant-    | TiffVariant-    | OldTiffVariant-    deriving Eq---- | Gif image constraint from spec-gif89a, code size max : 12 bits.-lzw :: TiffVariant -> Int -> Int -> Int -> M.STVector s Word8-    -> BoolReader s ()-lzw variant nMaxBitKeySize initialKeySize initialWriteIdx outVec = do-    -- Allocate buffer of maximum size.-    lzwData <- lift (M.replicate maxDataSize 0) >>= resetArray-    lzwOffsetTable <- lift (M.replicate tableEntryCount 0) >>= resetArray-    lzwSizeTable <- lift $ M.replicate tableEntryCount 0-    lift $ lzwSizeTable `M.set` 1--    let firstVal code = do-            dataOffset <- lzwOffsetTable ..!!!.. code-            lzwData ..!!!.. dataOffset--        writeString at code = do-            dataOffset <- lzwOffsetTable ..!!!.. code-            dataSize   <- lzwSizeTable   ..!!!.. code--            when (at + dataSize <= maxWrite) $-                 duplicateData lzwData outVec dataOffset dataSize at--            return dataSize--        addString pos at code val = do-            dataOffset <- lzwOffsetTable ..!!!.. code-            dataSize   <- lzwSizeTable   ..!!!.. code--            when (pos < tableEntryCount) $ do-              (lzwOffsetTable ..<-.. pos) at-              (lzwSizeTable ..<-.. pos) $ dataSize + 1--            when (at + dataSize + 1 <= maxDataSize) $ do-              duplicateData lzwData lzwData dataOffset dataSize at-              (lzwData ..<-.. (at + dataSize)) val--            return $ dataSize + 1--        maxWrite = M.length outVec-        loop outWriteIdx writeIdx dicWriteIdx codeSize oldCode code-          | outWriteIdx >= maxWrite = return ()-          | code == endOfInfo = return ()-          | code == clearCode = do-              toOutput <- getNextCode startCodeSize-              unless (toOutput == endOfInfo) $ do-                dataSize <- writeString outWriteIdx toOutput-                getNextCode startCodeSize >>=-                  loop (outWriteIdx + dataSize)-                       firstFreeIndex firstFreeIndex startCodeSize toOutput--          | otherwise =  do-              (written, dicAdd) <--                   if code >= writeIdx then do-                     c <- firstVal oldCode-                     wroteSize <- writeString outWriteIdx oldCode-                     (outVec ..<-.. (outWriteIdx + wroteSize)) c-                     addedSize <- addString writeIdx dicWriteIdx oldCode c-                     return (wroteSize + 1, addedSize)-                   else do-                     wroteSize <- writeString outWriteIdx code-                     c <- firstVal code-                     addedSize <- addString writeIdx dicWriteIdx oldCode c-                     return (wroteSize, addedSize)--              let new_code_size = updateCodeSize codeSize $ writeIdx + 1-              getNextCode new_code_size >>=-                loop (outWriteIdx + written)-                     (writeIdx + 1)-                     (dicWriteIdx + dicAdd)-                     new_code_size-                     code--    getNextCode startCodeSize >>=-        loop initialWriteIdx firstFreeIndex firstFreeIndex startCodeSize 0--  where tableEntryCount =  2 ^ min 12 nMaxBitKeySize-        maxDataSize = tableEntryCount `div` 2 * (1 + tableEntryCount) + 1--        isNewTiff = variant == TiffVariant-        (switchOffset,  isTiffVariant) = case variant of-            GifVariant -> (0, False)-            TiffVariant -> (1, True)-            OldTiffVariant -> (0, True)--        initialElementCount = 2 ^ initialKeySize :: Int-        clearCode | isTiffVariant = 256-                  | otherwise = initialElementCount--        endOfInfo | isTiffVariant = 257-                  | otherwise = clearCode + 1--        startCodeSize -                  | isTiffVariant = initialKeySize-                  | otherwise = initialKeySize + 1--        firstFreeIndex = endOfInfo + 1--        resetArray a = lift $ rangeSetter initialElementCount a--        updateCodeSize codeSize writeIdx-            | writeIdx == 2 ^ codeSize - switchOffset = min 12 $ codeSize + 1-            | otherwise = codeSize--        getNextCode s -            | isNewTiff = fromIntegral <$> getNextBitsMSBFirst s-            | otherwise = fromIntegral <$> getNextBitsLSBFirst s-
− src/Codec/Picture/Gif/LZWEncoding.hs
@@ -1,101 +0,0 @@-{-# LANGUAGE BangPatterns, CPP #-}
-module Codec.Picture.Gif.LZWEncoding( lzwEncode ) where
-
-#if !MIN_VERSION_base(4,8,0)
-import Control.Applicative( (<$>) )
-import Data.Monoid( mempty )
-#endif
-
-import Control.Monad.ST( runST )
-import qualified Data.ByteString.Lazy as L
-import Data.Maybe( fromMaybe )
-import Data.Word( Word8 )
-
-#if MIN_VERSION_containers(0,5,0)
-import qualified Data.IntMap.Strict as I
-#else
-import qualified Data.IntMap as I
-#endif
-import qualified Data.Vector.Storable as V
-
-import Codec.Picture.BitWriter
-
-type Trie = I.IntMap TrieNode
-
-data TrieNode = TrieNode
-    { trieIndex :: {-# UNPACK #-} !Int
-    , trieSub   :: !Trie
-    }
-
-emptyNode :: TrieNode
-emptyNode = TrieNode
-    { trieIndex = -1
-    , trieSub = mempty
-    }
-
-initialTrie :: Trie
-initialTrie = I.fromList
-    [(i, emptyNode { trieIndex = i }) | i <- [0 .. 255]]
-
-lookupUpdate :: V.Vector Word8 -> Int -> Int -> Trie -> (Int, Int, Trie)
-lookupUpdate vector freeIndex firstIndex trie =
-    matchUpdate $ go trie 0 firstIndex 
-  where
-    matchUpdate (lzwOutputIndex, nextReadIndex, sub) =
-        (lzwOutputIndex, nextReadIndex, fromMaybe trie sub)
-
-    maxi = V.length vector
-    go !currentTrie !prevIndex !index
-      | index >= maxi = (prevIndex, index, Nothing)
-      | otherwise = case I.lookup val currentTrie of
-          Just (TrieNode ix subTable) ->
-              let (lzwOutputIndex, nextReadIndex, newTable) =
-                        go subTable ix $ index + 1
-                  tableUpdater t =
-                      I.insert val (TrieNode ix t) currentTrie
-              in
-              (lzwOutputIndex, nextReadIndex, tableUpdater <$> newTable)
-
-          Nothing | index == maxi -> (prevIndex, index, Nothing)
-                  | otherwise -> (prevIndex, index, Just $ I.insert val newNode currentTrie)
-
-      where val = fromIntegral $ vector `V.unsafeIndex` index
-            newNode = emptyNode { trieIndex = freeIndex }
-
-lzwEncode :: Int -> V.Vector Word8 -> L.ByteString
-lzwEncode initialKeySize vec = runST $ do
-    bitWriter <- newWriteStateRef 
-
-    let updateCodeSize 12 writeIdx _
-            | writeIdx == 2 ^ (12 :: Int) - 1 = do
-               writeBitsGif bitWriter (fromIntegral clearCode) 12
-               return (startCodeSize, firstFreeIndex, initialTrie)
-
-        updateCodeSize codeSize writeIdx trie
-            | writeIdx == 2 ^ codeSize =
-                return (codeSize + 1, writeIdx + 1, trie)
-            | otherwise = return (codeSize, writeIdx + 1, trie)
-
-        go readIndex (codeSize, _, _) | readIndex >= maxi =
-            writeBitsGif bitWriter (fromIntegral endOfInfo) codeSize
-        go !readIndex (!codeSize, !writeIndex, !trie) = do
-            let (indexToWrite, endIndex, trie') =
-                    lookuper writeIndex readIndex trie
-            writeBitsGif bitWriter (fromIntegral indexToWrite) codeSize
-            updateCodeSize codeSize writeIndex trie'
-                >>= go endIndex 
-
-    writeBitsGif bitWriter (fromIntegral clearCode) startCodeSize
-    go 0 (startCodeSize, firstFreeIndex, initialTrie)
-
-    finalizeBoolWriterGif bitWriter
-  where
-    maxi = V.length vec
-
-    startCodeSize = initialKeySize + 1
-
-    clearCode = 2 ^ initialKeySize :: Int
-    endOfInfo = clearCode + 1
-    firstFreeIndex = endOfInfo + 1
-    
-    lookuper = lookupUpdate vec
src/Codec/Picture/Jpg.hs view
@@ -52,14 +52,14 @@ import Codec.Picture.Metadata( Metadatas
                              , SourceFormat( SourceJpeg )
                              , basicMetadata )
-import Codec.Picture.Tiff.Types
-import Codec.Picture.Tiff.Metadata
-import Codec.Picture.Jpg.Types
-import Codec.Picture.Jpg.Common
-import Codec.Picture.Jpg.Progressive
-import Codec.Picture.Jpg.DefaultTable
-import Codec.Picture.Jpg.FastDct
-import Codec.Picture.Jpg.Metadata
+import Codec.Picture.Tiff.Internal.Types
+import Codec.Picture.Tiff.Internal.Metadata
+import Codec.Picture.Jpg.Internal.Types
+import Codec.Picture.Jpg.Internal.Common
+import Codec.Picture.Jpg.Internal.Progressive
+import Codec.Picture.Jpg.Internal.DefaultTable
+import Codec.Picture.Jpg.Internal.FastDct
+import Codec.Picture.Jpg.Internal.Metadata
 
 quantize :: MacroBlock Int16 -> MutableMacroBlock s Int32
          -> ST s (MutableMacroBlock s Int32)
− src/Codec/Picture/Jpg/Common.hs
@@ -1,240 +0,0 @@-{-# 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
-
--- | Same as for DcCoefficient, to provide nicer type signatures
-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
-      -- | When in progressive mode, we can have many
-      -- color in a scan or only one. The indices changes
-      -- on this fact, when mixed, there is whole 
-      -- MCU for all color components, spanning multiple
-      -- block lines. With only one color component we use
-      -- the normal raster order.
-    , 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) {-  do
-  bits <- replicateM 8 getNextBitJpg
-  if bits == replicate 8 True
-     then do
-         marker <- replicateM 8 getNextBitJpg
-         return $ packInt marker
-     else return (-1)
-        -}
-
-{-# INLINE decodeInt #-}
-decodeInt :: Int -> BoolReader s Int32
-decodeInt ssss = do
-    signBit <- getNextBitJpg
-    let dataRange = 1 `unsafeShiftL` fromIntegral (ssss - 1)
-        leftBitCount = ssss - 1
-    -- First following bits store the sign of the coefficient, and counted in
-    -- SSSS, so the bit count for the int, is 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)
-
--- | Apply a quantization matrix to a macroblock
-{-# 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
-
--- | Unpack an int of the given size encoded from MSB to LSB.
-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
-
--- | Given a size coefficient (how much a pixel span horizontally
--- and vertically), the position of the macroblock, return a list
--- of indices and value to be stored in an array (like the final
--- image)
-unpackMacroBlock :: Int    -- ^ Component count
-                 -> Int -- ^ Width coefficient
-                 -> Int -- ^ Height coefficient
-                 -> Int -- ^ Component index
-                 -> Int -- ^ x
-                 -> Int -- ^ y
-                 -> 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)
-
--- | This is one of the most important function of the decoding,
--- it form the barebone decoding pipeline for macroblock. It's all
--- there is to know for macro block transformation
-decodeMacroBlock :: MacroBlock DctCoefficients
-                 -> MutableMacroBlock s Int16
-                 -> MutableMacroBlock s Int16
-                 -> ST s (MutableMacroBlock s Int16)
-decodeMacroBlock quantizationTable zigZagBlock block =
-    deQuantize quantizationTable block >>= zigZagReorder zigZagBlock
-                                       >>= inverseDirectCosineTransform
-
− src/Codec/Picture/Jpg/DefaultTable.hs
@@ -1,294 +0,0 @@-{-# LANGUAGE TupleSections #-}
-{-# LANGUAGE FlexibleContexts #-}
--- | Module used by the jpeg decoder internally, shouldn't be used
--- in user code.
-module Codec.Picture.Jpg.DefaultTable( DctComponent( .. )
-                                     , HuffmanTree( .. )
-                                     , HuffmanTable
-                                     , HuffmanPackedTree
-                                     , MacroBlock
-                                     , QuantificationTable
-                                     , HuffmanWriterCode 
-                                     , scaleQuantisationMatrix
-                                     , makeMacroBlock
-                                     , makeInverseTable
-                                     , buildHuffmanTree
-                                     , packHuffmanTree
-                                     , huffmanPackedDecode
-
-                                     , defaultChromaQuantizationTable
-
-                                     , defaultLumaQuantizationTable
-
-                                     , defaultAcChromaHuffmanTree
-                                     , defaultAcChromaHuffmanTable
-
-                                     , defaultAcLumaHuffmanTree 
-                                     , defaultAcLumaHuffmanTable 
-
-                                     , defaultDcChromaHuffmanTree 
-                                     , defaultDcChromaHuffmanTable
-
-                                     , defaultDcLumaHuffmanTree
-                                     , defaultDcLumaHuffmanTable
-                                     ) where
-
-import Data.Int( Int16 )
-import Foreign.Storable ( Storable )
-import Control.Monad.ST( runST )
-import qualified Data.Vector.Storable as SV
-import qualified Data.Vector as V
-import Data.Bits( unsafeShiftL, (.|.), (.&.) )
-import Data.Word( Word8, Word16 )
-import Data.List( foldl' )
-import qualified Data.Vector.Storable.Mutable as M
-
-import Codec.Picture.BitWriter
-
--- | Tree storing the code used for huffman encoding.
-data HuffmanTree = Branch HuffmanTree HuffmanTree -- ^ If bit is 0 take the first subtree, if 1, the right.
-                 | Leaf Word8       -- ^ We should output the value
-                 | Empty            -- ^ no value present
-                 deriving (Eq, Show)
-
-type HuffmanPackedTree = SV.Vector Word16
-
-type HuffmanWriterCode = V.Vector (Word8, Word16)
-
-packHuffmanTree :: HuffmanTree -> HuffmanPackedTree
-packHuffmanTree tree = runST $ do
-    table <- M.replicate 512 0x8000
-    let aux (Empty) idx = return $ idx + 1
-        aux (Leaf v) idx = do
-            (table `M.unsafeWrite` idx) $ fromIntegral v .|. 0x4000
-            return $ idx + 1
-
-        aux (Branch i1@(Leaf _) i2@(Leaf _)) idx =
-            aux i1 idx >>= aux i2
-
-        aux (Branch i1@(Leaf _) i2) idx = do
-            _ <- aux i1 idx
-            ix2 <- aux i2 $ idx + 2
-            (table `M.unsafeWrite` (idx + 1)) $ fromIntegral $ idx + 2
-            return ix2
-
-        aux (Branch i1 i2@(Leaf _)) idx = do
-            ix1 <- aux i1 (idx + 2)
-            _ <- aux i2 (idx + 1)
-            (table `M.unsafeWrite` idx) . fromIntegral $ idx + 2
-            return ix1
-
-        aux (Branch i1 i2) idx = do
-            ix1 <- aux i1 (idx + 2)
-            ix2 <- aux i2 ix1
-            (table `M.unsafeWrite` idx) (fromIntegral $ idx + 2)
-            (table `M.unsafeWrite` (idx + 1)) (fromIntegral ix1)
-            return ix2
-    _ <- aux tree 0
-    SV.unsafeFreeze table
-
-makeInverseTable :: HuffmanTree -> HuffmanWriterCode
-makeInverseTable t = V.replicate 255 (0,0) V.// inner 0 0 t
-  where inner _     _     Empty   = []
-        inner depth code (Leaf v) = [(fromIntegral v, (depth, code))]
-        inner depth code (Branch l r) =
-          inner (depth + 1) shifted l ++ inner (depth + 1) (shifted .|. 1) r
-            where shifted = code `unsafeShiftL` 1
-
--- | Represent a compact array of 8 * 8 values. The size
--- is not guarenteed by type system, but if makeMacroBlock is
--- used, everything should be fine size-wise
-type MacroBlock a = SV.Vector a
-
-type QuantificationTable = MacroBlock Int16
-
--- | Helper function to create pure macro block of the good size.
-makeMacroBlock :: (Storable a) => [a] -> MacroBlock a
-makeMacroBlock = SV.fromListN 64
-
--- | Enumeration used to search in the tables for different components.
-data DctComponent = DcComponent | AcComponent
-    deriving (Eq, Show)
-
--- | Transform parsed coefficients from the jpeg header to a
--- tree which can be used to decode data.
-buildHuffmanTree :: [[Word8]] -> HuffmanTree
-buildHuffmanTree table = foldl' insertHuffmanVal Empty
-                       . concatMap (\(i, t) -> map (i + 1,) t)
-                       $ zip ([0..] :: [Int]) table
-  where isTreeFullyDefined Empty = False
-        isTreeFullyDefined (Leaf _) = True
-        isTreeFullyDefined (Branch l r) = isTreeFullyDefined l && isTreeFullyDefined r
-
-        insertHuffmanVal Empty (0, val) = Leaf val
-        insertHuffmanVal Empty (d, val) = Branch (insertHuffmanVal Empty (d - 1, val)) Empty
-        insertHuffmanVal (Branch l r) (d, val)
-            | isTreeFullyDefined l = Branch l (insertHuffmanVal r (d - 1, val))
-            | otherwise            = Branch (insertHuffmanVal l (d - 1, val)) r
-        insertHuffmanVal (Leaf _) _ = error "Inserting in value, shouldn't happen"
-
-scaleQuantisationMatrix :: Int -> QuantificationTable -> QuantificationTable 
-scaleQuantisationMatrix quality
-    | quality < 0 = scaleQuantisationMatrix 0
-        -- shouldn't show much difference than with 1,
-        -- but hey, at least we're complete
-    | quality == 0 = SV.map (scale (10000 :: Int))
-    | quality < 50 = let qq = 5000 `div` quality
-                     in SV.map (scale qq)
-    | otherwise    = SV.map (scale q)
-          where q = 200 - quality * 2
-                scale coeff i = fromIntegral . min 255 
-                                             . max 1 
-                                             $ fromIntegral i * coeff `div` 100
-
-huffmanPackedDecode :: HuffmanPackedTree -> BoolReader s Word8
-huffmanPackedDecode table = getNextBitJpg >>= aux 0
-  where aux idx b
-            | (v .&. 0x8000) /= 0 = return  0
-            | (v .&. 0x4000) /= 0 = return . fromIntegral $ v .&. 0xFF
-            | otherwise = getNextBitJpg >>= aux v
-          where tableIndex | b = idx + 1
-                           | otherwise = idx
-                v = table `SV.unsafeIndex` fromIntegral tableIndex
-
-defaultLumaQuantizationTable :: QuantificationTable
-defaultLumaQuantizationTable = makeMacroBlock
-    [16, 11, 10, 16,  24,  40,  51,  61
-    ,12, 12, 14, 19,  26,  58,  60,  55
-    ,14, 13, 16, 24,  40,  57,  69,  56
-    ,14, 17, 22, 29,  51,  87,  80,  62
-    ,18, 22, 37, 56,  68, 109, 103,  77
-    ,24, 35, 55, 64,  81, 104, 113,  92
-    ,49, 64, 78, 87, 103, 121, 120, 101
-    ,72, 92, 95, 98, 112, 100, 103,  99
-    ]
-
-defaultChromaQuantizationTable :: QuantificationTable
-defaultChromaQuantizationTable = makeMacroBlock
-    [17, 18, 24, 47, 99, 99, 99, 99
-    ,18, 21, 26, 66, 99, 99, 99, 99
-    ,24, 26, 56, 99, 99, 99, 99, 99
-    ,47, 66, 99, 99, 99, 99, 99, 99
-    ,99, 99, 99, 99, 99, 99, 99, 99
-    ,99, 99, 99, 99, 99, 99, 99, 99
-    ,99, 99, 99, 99, 99, 99, 99, 99
-    ,99, 99, 99, 99, 99, 99, 99, 99
-    ]
-
-defaultDcLumaHuffmanTree :: HuffmanTree
-defaultDcLumaHuffmanTree = buildHuffmanTree defaultDcLumaHuffmanTable
-
--- | From the Table K.3 of ITU-81 (p153)
-defaultDcLumaHuffmanTable :: HuffmanTable
-defaultDcLumaHuffmanTable =
-    [ []
-    , [0]
-    , [1, 2, 3, 4, 5]
-    , [6]
-    , [7]
-    , [8]
-    , [9]
-    , [10]
-    , [11]
-    , []
-    , []
-    , []
-    , []
-    , []
-    , []
-    , []
-    ]
-
-defaultDcChromaHuffmanTree :: HuffmanTree
-defaultDcChromaHuffmanTree = buildHuffmanTree defaultDcChromaHuffmanTable
-
--- | From the Table K.4 of ITU-81 (p153)
-defaultDcChromaHuffmanTable :: HuffmanTable
-defaultDcChromaHuffmanTable = 
-    [ []
-    , [0, 1, 2]
-    , [3]
-    , [4]
-    , [5]
-    , [6]
-    , [7]
-    , [8]
-    , [9]
-    , [10]
-    , [11]
-    , []
-    , []
-    , []
-    , []
-    , []
-    ]
-
-defaultAcLumaHuffmanTree :: HuffmanTree
-defaultAcLumaHuffmanTree = buildHuffmanTree defaultAcLumaHuffmanTable
-
--- | From the Table K.5 of ITU-81 (p154)
-defaultAcLumaHuffmanTable :: HuffmanTable
-defaultAcLumaHuffmanTable =
-    [ []
-    , [0x01, 0x02]
-    , [0x03]
-    , [0x00, 0x04, 0x11]
-    , [0x05, 0x12, 0x21]
-    , [0x31, 0x41]
-    , [0x06, 0x13, 0x51, 0x61]
-    , [0x07, 0x22, 0x71]
-    , [0x14, 0x32, 0x81, 0x91, 0xA1]
-    , [0x08, 0x23, 0x42, 0xB1, 0xC1]
-    , [0x15, 0x52, 0xD1, 0xF0]
-    , [0x24, 0x33, 0x62, 0x72]
-    , []
-    , []
-    , [0x82]
-    , [0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35
-      ,0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54
-      ,0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73
-      ,0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A
-      ,0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7
-      ,0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4
-      ,0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA
-      ,0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5
-      ,0xF6, 0xF7, 0xF8, 0xF9, 0xFA]
-    ]
-
-type HuffmanTable = [[Word8]]
-
-defaultAcChromaHuffmanTree :: HuffmanTree
-defaultAcChromaHuffmanTree = buildHuffmanTree defaultAcChromaHuffmanTable 
-
-defaultAcChromaHuffmanTable :: HuffmanTable
-defaultAcChromaHuffmanTable = 
-    [ []
-    , [0x00, 0x01]
-    , [0x02]
-    , [0x03, 0x11]
-    , [0x04, 0x05, 0x21, 0x31]
-    , [0x06, 0x12, 0x41, 0x51]
-    , [0x07, 0x61, 0x71]
-    , [0x13, 0x22, 0x32, 0x81]
-    , [0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1]
-    , [0x09, 0x23, 0x33, 0x52, 0xF0]
-    , [0x15, 0x62, 0x72, 0xD1]
-    , [0x0A, 0x16, 0x24, 0x34]
-    , []
-    , [0xE1]
-    , [0x25, 0xF1]
-    , [ 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35
-      , 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47
-      , 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59
-      , 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73
-      , 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84
-      , 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95
-      , 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6
-      , 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7
-      , 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8
-      , 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9
-      , 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA
-      , 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA
-      ]
-    ]
-
− src/Codec/Picture/Jpg/FastDct.hs
@@ -1,218 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE TypeFamilies #-}-module Codec.Picture.Jpg.FastDct( referenceDct, fastDctLibJpeg ) where--#if !MIN_VERSION_base(4,8,0)-import Control.Applicative( (<$>) )-#endif--import Data.Int( Int16, Int32 )-import Data.Bits( unsafeShiftR, unsafeShiftL )-import Control.Monad.ST( ST )--import qualified Data.Vector.Storable.Mutable as M--import Codec.Picture.Jpg.Types-import Control.Monad( forM, forM_ )---- | Reference implementation of the DCT, directly implementing the formula--- of ITU-81. It's slow as hell, perform to many operations, but is accurate--- and a good reference point.-referenceDct :: MutableMacroBlock s Int32-             -> MutableMacroBlock s Int16-             -> ST s (MutableMacroBlock s Int32)-referenceDct workData block = do-  forM_ [(u, v) | u <- [0 :: Int .. dctBlockSize - 1], v <- [0..dctBlockSize - 1]] $ \(u,v) -> do-    val <- at (u,v)-    (workData `M.unsafeWrite` (v * dctBlockSize + u)) . truncate $ (1 / 4) * c u * c v * val--  return workData- where -- at :: (Int, Int) -> ST s Float-   at (u,v) = do-     toSum <--        forM [(x,y) | x <- [0..dctBlockSize - 1], y <- [0..dctBlockSize - 1 :: Int]] $ \(x,y) -> do-            sample <- fromIntegral <$> (block `M.unsafeRead` (y * dctBlockSize + x))-            return $ sample * cos ((2 * fromIntegral x + 1) * fromIntegral u * (pi :: Float)/ 16) -                            * cos ((2 * fromIntegral y + 1) * fromIntegral v * pi / 16)-     return $ sum toSum--   c 0 = 1 / sqrt 2-   c _ = 1--pASS1_BITS, cONST_BITS :: Int-cONST_BITS = 13-pASS1_BITS =  2---fIX_0_298631336, fIX_0_390180644, fIX_0_541196100,-    fIX_0_765366865, fIX_0_899976223, fIX_1_175875602,-    fIX_1_501321110, fIX_1_847759065, fIX_1_961570560,-    fIX_2_053119869, fIX_2_562915447, fIX_3_072711026 :: Int32-fIX_0_298631336 = 2446    -- FIX(0.298631336) */-fIX_0_390180644 = 3196    -- FIX(0.390180644) */-fIX_0_541196100 = 4433    -- FIX(0.541196100) */-fIX_0_765366865 = 6270    -- FIX(0.765366865) */-fIX_0_899976223 = 7373    -- FIX(0.899976223) */-fIX_1_175875602 = 9633    -- FIX(1.175875602) */-fIX_1_501321110 = 12299    -- FIX(1.501321110) */-fIX_1_847759065 = 15137    -- FIX(1.847759065) */-fIX_1_961570560 = 16069    -- FIX(1.961570560) */-fIX_2_053119869 = 16819    -- FIX(2.053119869) */-fIX_2_562915447 = 20995    -- FIX(2.562915447) */-fIX_3_072711026 = 25172    -- FIX(3.072711026) */--cENTERJSAMPLE :: Int32-cENTERJSAMPLE = 128---- | Fast DCT extracted from libjpeg-fastDctLibJpeg :: MutableMacroBlock s Int32-        -> MutableMacroBlock s Int16-        -> ST s (MutableMacroBlock s Int32)-fastDctLibJpeg workData sample_block = do- firstPass workData  0- secondPass workData 7- {-_ <- mutate (\_ a -> a `quot` 8) workData-}- return workData-  where -- Pass 1: process rows.-        -- Note results are scaled up by sqrt(8) compared to a true DCT;-        -- furthermore, we scale the results by 2**PASS1_BITS.-        firstPass         _ i | i == dctBlockSize = return ()-        firstPass dataBlock i = do-            let baseIdx = i * dctBlockSize-                readAt idx = fromIntegral <$> sample_block `M.unsafeRead` (baseIdx + idx)-                mult = (*)-                writeAt idx = dataBlock `M.unsafeWrite` (baseIdx + idx)-                writeAtPos idx n = (dataBlock `M.unsafeWrite` (baseIdx + idx))-                                    (n `unsafeShiftR` (cONST_BITS - pASS1_BITS))--            blk0 <- readAt 0-            blk1 <- readAt 1-            blk2 <- readAt 2-            blk3 <- readAt 3-            blk4 <- readAt 4-            blk5 <- readAt 5-            blk6 <- readAt 6-            blk7 <- readAt 7--            let tmp0 = blk0 + blk7-                tmp1 = blk1 + blk6-                tmp2 = blk2 + blk5-                tmp3 = blk3 + blk4--                tmp10 = tmp0 + tmp3-                tmp12 = tmp0 - tmp3-                tmp11 = tmp1 + tmp2-                tmp13 = tmp1 - tmp2--                tmp0' = blk0 - blk7-                tmp1' = blk1 - blk6-                tmp2' = blk2 - blk5-                tmp3' = blk3 - blk4--            -- Stage 4 and output-            writeAt 0 $ (tmp10 + tmp11 - dctBlockSize * cENTERJSAMPLE) `unsafeShiftL` pASS1_BITS-            writeAt 4 $ (tmp10 - tmp11) `unsafeShiftL` pASS1_BITS--            let z1 = mult (tmp12 + tmp13) fIX_0_541196100-                     + (1 `unsafeShiftL` (cONST_BITS - pASS1_BITS - 1))--            writeAtPos 2 $ z1 + mult tmp12 fIX_0_765366865-            writeAtPos 6 $ z1 - mult tmp13 fIX_1_847759065--            let tmp10' = tmp0' + tmp3'-                tmp11' = tmp1' + tmp2'-                tmp12' = tmp0' + tmp2'-                tmp13' = tmp1' + tmp3'-                z1' = mult (tmp12' + tmp13') fIX_1_175875602 --  c3 */-                        -- Add fudge factor here for final descale. */-                        + (1 `unsafeShiftL` (cONST_BITS - pASS1_BITS-1))-                tmp0'' = mult tmp0' fIX_1_501321110-                tmp1'' = mult tmp1' fIX_3_072711026-                tmp2'' = mult tmp2' fIX_2_053119869-                tmp3'' = mult tmp3' fIX_0_298631336--                tmp10'' = mult tmp10' (- fIX_0_899976223)-                tmp11'' = mult tmp11' (- fIX_2_562915447)-                tmp12'' = mult tmp12' (- fIX_0_390180644) + z1'-                tmp13'' = mult tmp13' (- fIX_1_961570560) + z1'--            writeAtPos 1 $ tmp0'' + tmp10'' + tmp12''-            writeAtPos 3 $ tmp1'' + tmp11'' + tmp13''-            writeAtPos 5 $ tmp2'' + tmp11'' + tmp12''-            writeAtPos 7 $ tmp3'' + tmp10'' + tmp13''--            firstPass dataBlock $ i + 1--        -- Pass 2: process columns.-        -- We remove the PASS1_BITS scaling, but leave the results scaled up-        -- by an overall factor of 8.-        secondPass :: M.STVector s Int32 -> Int -> ST s ()-        secondPass _     (-1) = return ()-        secondPass block i = do-            let readAt idx = block `M.unsafeRead` ((7 - i) + idx * dctBlockSize)-                mult = (*)-                writeAt idx = block `M.unsafeWrite` (dctBlockSize * idx + (7 - i))-                writeAtPos idx n = (block `M.unsafeWrite` (dctBlockSize * idx + (7 - i))) $ n `unsafeShiftR` (cONST_BITS + pASS1_BITS + 3)-            blk0 <- readAt 0-            blk1 <- readAt 1-            blk2 <- readAt 2-            blk3 <- readAt 3-            blk4 <- readAt 4-            blk5 <- readAt 5-            blk6 <- readAt 6-            blk7 <- readAt 7--            let tmp0 = blk0 + blk7-                tmp1 = blk1 + blk6-                tmp2 = blk2 + blk5-                tmp3 = blk3 + blk4--                -- Add fudge factor here for final descale. */-                tmp10 = tmp0 + tmp3 + (1 `unsafeShiftL` (pASS1_BITS-1))-                tmp12 = tmp0 - tmp3-                tmp11 = tmp1 + tmp2-                tmp13 = tmp1 - tmp2--                tmp0' = blk0 - blk7-                tmp1' = blk1 - blk6-                tmp2' = blk2 - blk5-                tmp3' = blk3 - blk4--            writeAt 0 $ (tmp10 + tmp11) `unsafeShiftR` (pASS1_BITS + 3)-            writeAt 4 $ (tmp10 - tmp11) `unsafeShiftR` (pASS1_BITS + 3)--            let z1 = mult (tmp12 + tmp13) fIX_0_541196100-                    + (1 `unsafeShiftL` (cONST_BITS + pASS1_BITS - 1))--            writeAtPos 2 $ z1 + mult tmp12 fIX_0_765366865-            writeAtPos 6 $ z1 - mult tmp13 fIX_1_847759065--            let tmp10' = tmp0' + tmp3'-                tmp11' = tmp1' + tmp2'-                tmp12' = tmp0' + tmp2'-                tmp13' = tmp1' + tmp3'--                z1' = mult (tmp12' + tmp13') fIX_1_175875602-                    -- Add fudge factor here for final descale. */-                   + 1 `unsafeShiftL` (cONST_BITS+pASS1_BITS-1);--                tmp0''  = mult tmp0'    fIX_1_501321110-                tmp1''  = mult tmp1'    fIX_3_072711026-                tmp2''  = mult tmp2'    fIX_2_053119869-                tmp3''  = mult tmp3'    fIX_0_298631336-                tmp10'' = mult tmp10' (- fIX_0_899976223)-                tmp11'' = mult tmp11' (- fIX_2_562915447)-                tmp12'' = mult tmp12' (- fIX_0_390180644)-                            + z1'-                tmp13'' = mult tmp13' (- fIX_1_961570560)-                            + z1'-            writeAtPos 1 $ tmp0'' + tmp10'' + tmp12''-            writeAtPos 3 $ tmp1'' + tmp11'' + tmp13''-            writeAtPos 5 $ tmp2'' + tmp11'' + tmp12''-            writeAtPos 7 $ tmp3'' + tmp10'' + tmp13''--            secondPass block (i - 1)--{-# ANN module "HLint: ignore Use camelCase" #-}-{-# ANN module "HLint: ignore Reduce duplication" #-}-
− src/Codec/Picture/Jpg/FastIdct.hs
@@ -1,229 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}
-{-# LANGUAGE TypeFamilies #-}
--- | Module providing a 'fast' implementation of IDCT
---
--- inverse two dimensional DCT, Chen-Wang algorithm       
--- (cf. IEEE ASSP-32, pp. 803-816, Aug. 1984)             
--- 32-bit integer arithmetic (8 bit coefficients)         
--- 11 mults, 29 adds per DCT                              
---                                      sE, 18.8.91       
---
--- coefficients extended to 12 bit for IEEE1180-1990      
--- compliance                           sE,  2.1.94       
---
--- this code assumes >> to be a two's-complement arithmetic
--- right shift: (-2)>>1 == -1 , (-3)>>1 == -2               
-module Codec.Picture.Jpg.FastIdct( MutableMacroBlock
-                                 , fastIdct
-                                 , mutableLevelShift
-                                 , createEmptyMutableMacroBlock
-                                 ) where
-
-import qualified Data.Vector.Storable as V
-import Control.Monad.ST( ST )
-import Data.Bits( unsafeShiftL, unsafeShiftR )
-import Data.Int( Int16 )
-import qualified Data.Vector.Storable.Mutable as M
-
-import Codec.Picture.Jpg.Types
-
-iclip :: V.Vector Int16
-iclip = V.fromListN 1024 [ val i| i <- [(-512) .. 511] ]
-    where val i | i < (-256) = -256
-                | i > 255    =  255
-                | otherwise  =  i
-
-data IDctStage = IDctStage { 
-        x0 :: {-# UNPACK #-} !Int,
-        x1 :: {-# UNPACK #-} !Int,
-        x2 :: {-# UNPACK #-} !Int,
-        x3 :: {-# UNPACK #-} !Int,
-        x4 :: {-# UNPACK #-} !Int,
-        x5 :: {-# UNPACK #-} !Int,
-        x6 :: {-# UNPACK #-} !Int,
-        x7 :: {-# UNPACK #-} !Int,
-        x8 :: {-# UNPACK #-} !Int
-    }
-
-w1, w2, w3, w5, w6, w7 :: Int
-w1 = 2841 -- 2048*sqrt(2)*cos(1*pi/16)
-w2 = 2676 -- 2048*sqrt(2)*cos(2*pi/16)
-w3 = 2408 -- 2048*sqrt(2)*cos(3*pi/16)
-w5 = 1609 -- 2048*sqrt(2)*cos(5*pi/16)
-w6 = 1108 -- 2048*sqrt(2)*cos(6*pi/16)
-w7 = 565  -- 2048*sqrt(2)*cos(7*pi/16)
-
--- row (horizontal) IDCT
---
---           7                       pi         1
--- dst[k] = sum c[l] * src[l] * cos( -- * ( k + - ) * l )
---          l=0                      8          2
---
--- where: c[0]    = 128
---        c[1..7] = 128*sqrt(2)
-idctRow :: MutableMacroBlock s Int16 -> Int ->  ST s ()
-idctRow blk idx = do
-  xx0 <- blk `M.unsafeRead` (0 + idx)
-  xx1 <- blk `M.unsafeRead` (4 + idx) 
-  xx2 <- blk `M.unsafeRead` (6 + idx)
-  xx3 <- blk `M.unsafeRead` (2 + idx)
-  xx4 <- blk `M.unsafeRead` (1 + idx)
-  xx5 <- blk `M.unsafeRead` (7 + idx)
-  xx6 <- blk `M.unsafeRead` (5 + idx)
-  xx7 <- blk `M.unsafeRead` (3 + idx)
-  let initialState = IDctStage { x0 = (fromIntegral xx0 `unsafeShiftL` 11) + 128
-                               , x1 =  fromIntegral xx1 `unsafeShiftL` 11
-                               , x2 =  fromIntegral xx2
-                               , x3 =  fromIntegral xx3
-                               , x4 =  fromIntegral xx4
-                               , x5 =  fromIntegral xx5
-                               , x6 =  fromIntegral xx6
-                               , x7 =  fromIntegral xx7
-                               , x8 = 0
-                               } 
-
-      firstStage c = c { x4 = x8'  + (w1 - w7) * x4 c
-                       , x5 = x8'  - (w1 + w7) * x5 c
-                       , x6 = x8'' - (w3 - w5) * x6 c
-                       , x7 = x8'' - (w3 + w5) * x7 c
-                       , x8 = x8''
-                       }
-          where x8' = w7 * (x4 c + x5 c)
-                x8'' = w3 * (x6 c + x7 c)
-
-      secondStage c = c { x0 = x0 c - x1 c
-                        , x8 = x0 c + x1 c 
-                        , x1 = x1''
-                        , x2 = x1' - (w2 + w6) * x2 c
-                        , x3 = x1' + (w2 - w6) * x3 c
-                        , x4 = x4 c - x6 c
-                        , x6 = x5 c + x7 c
-                        , x5 = x5 c - x7 c
-                        }
-            where x1'  = w6 * (x3 c + x2 c)
-                  x1'' = x4 c + x6 c
-
-      thirdStage c = c { x7 = x8 c + x3 c
-                       , x8 = x8 c - x3 c
-                       , x3 = x0 c + x2 c
-                       , x0 = x0 c - x2 c
-                       , x2 = (181 * (x4 c + x5 c) + 128) `unsafeShiftR` 8
-                       , x4 = (181 * (x4 c - x5 c) + 128) `unsafeShiftR` 8
-                       }
-      scaled c = c { x0 = (x7 c + x1 c) `unsafeShiftR` 8
-                   , x1 = (x3 c + x2 c) `unsafeShiftR` 8
-                   , x2 = (x0 c + x4 c) `unsafeShiftR` 8
-                   , x3 = (x8 c + x6 c) `unsafeShiftR` 8
-                   , x4 = (x8 c - x6 c) `unsafeShiftR` 8
-                   , x5 = (x0 c - x4 c) `unsafeShiftR` 8
-                   , x6 = (x3 c - x2 c) `unsafeShiftR` 8
-                   , x7 = (x7 c - x1 c) `unsafeShiftR` 8
-                   }
-      transformed = scaled . thirdStage . secondStage $ firstStage initialState
-
-  (blk `M.unsafeWrite` (0 + idx)) . fromIntegral $ x0 transformed
-  (blk `M.unsafeWrite` (1 + idx)) . fromIntegral $ x1 transformed
-  (blk `M.unsafeWrite` (2 + idx)) . fromIntegral $ x2 transformed
-  (blk `M.unsafeWrite` (3 + idx)) . fromIntegral $ x3 transformed
-  (blk `M.unsafeWrite` (4 + idx)) . fromIntegral $ x4 transformed
-  (blk `M.unsafeWrite` (5 + idx)) . fromIntegral $ x5 transformed
-  (blk `M.unsafeWrite` (6 + idx)) . fromIntegral $ x6 transformed
-  (blk `M.unsafeWrite` (7 + idx)) . fromIntegral $ x7 transformed
-
--- column (vertical) IDCT
---
---             7                         pi         1
--- dst[8*k] = sum c[l] * src[8*l] * cos( -- * ( k + - ) * l )
---            l=0                        8          2
---
--- where: c[0]    = 1/1024
---        c[1..7] = (1/1024)*sqrt(2)
---
-idctCol :: MutableMacroBlock s Int16 -> Int -> ST s ()
-idctCol blk idx = do
-  xx0 <- blk `M.unsafeRead` (    0 + idx)
-  xx1 <- blk `M.unsafeRead` (8 * 4 + idx)
-  xx2 <- blk `M.unsafeRead` (8 * 6 + idx)
-  xx3 <- blk `M.unsafeRead` (8 * 2 + idx)
-  xx4 <- blk `M.unsafeRead` (8     + idx)
-  xx5 <- blk `M.unsafeRead` (8 * 7 + idx)
-  xx6 <- blk `M.unsafeRead` (8 * 5 + idx)
-  xx7 <- blk `M.unsafeRead` (8 * 3 + idx)
-  let initialState = IDctStage { x0 = (fromIntegral xx0 `unsafeShiftL` 8) + 8192
-                               , x1 =  fromIntegral xx1 `unsafeShiftL` 8
-                               , x2 =  fromIntegral xx2
-                               , x3 =  fromIntegral xx3
-                               , x4 =  fromIntegral xx4
-                               , x5 =  fromIntegral xx5
-                               , x6 =  fromIntegral xx6
-                               , x7 =  fromIntegral xx7
-                               , x8 = 0
-                               } 
-      firstStage c = c { x4 = (x8'  + (w1 - w7) * x4 c) `unsafeShiftR` 3
-                       , x5 = (x8'  - (w1 + w7) * x5 c) `unsafeShiftR` 3
-                       , x6 = (x8'' - (w3 - w5) * x6 c) `unsafeShiftR` 3
-                       , x7 = (x8'' - (w3 + w5) * x7 c) `unsafeShiftR` 3
-                       , x8 = x8''
-                       }
-          where x8'  = w7 * (x4 c + x5 c) + 4
-                x8'' = w3 * (x6 c + x7 c) + 4
-
-      secondStage c = c { x8 = x0 c + x1 c
-                        , x0 = x0 c - x1 c
-                        , x2 = (x1' - (w2 + w6) * x2 c) `unsafeShiftR` 3
-                        , x3 = (x1' + (w2 - w6) * x3 c) `unsafeShiftR` 3
-                        , x4 = x4 c - x6 c
-                        , x1 = x1''
-                        , x6 = x5 c + x7 c
-                        , x5 = x5 c - x7 c
-                        }
-          where x1'  = w6 * (x3 c + x2 c) + 4
-                x1'' = x4 c + x6 c
-  
-      thirdStage c = c { x7 = x8 c + x3 c
-                       , x8 = x8 c - x3 c
-                       , x3 = x0 c + x2 c
-                       , x0 = x0 c - x2 c
-                       , x2 = (181 * (x4 c + x5 c) + 128) `unsafeShiftR` 8
-                       , x4 = (181 * (x4 c - x5 c) + 128) `unsafeShiftR` 8
-                       }
-
-      clip i | i < 511 = if i > -512 then iclip `V.unsafeIndex` (i + 512)
-                                    else iclip `V.unsafeIndex` 0
-            
-             | otherwise = iclip `V.unsafeIndex` 1023
-
-      f = thirdStage . secondStage $ firstStage initialState
-  (blk `M.unsafeWrite` (idx + 8*0)) . clip $ (x7 f + x1 f) `unsafeShiftR` 14
-  (blk `M.unsafeWrite` (idx + 8  )) . clip $ (x3 f + x2 f) `unsafeShiftR` 14
-  (blk `M.unsafeWrite` (idx + 8*2)) . clip $ (x0 f + x4 f) `unsafeShiftR` 14
-  (blk `M.unsafeWrite` (idx + 8*3)) . clip $ (x8 f + x6 f) `unsafeShiftR` 14
-  (blk `M.unsafeWrite` (idx + 8*4)) . clip $ (x8 f - x6 f) `unsafeShiftR` 14
-  (blk `M.unsafeWrite` (idx + 8*5)) . clip $ (x0 f - x4 f) `unsafeShiftR` 14
-  (blk `M.unsafeWrite` (idx + 8*6)) . clip $ (x3 f - x2 f) `unsafeShiftR` 14
-  (blk `M.unsafeWrite` (idx + 8*7)) . clip $ (x7 f - x1 f) `unsafeShiftR` 14
-
-
-{-# INLINE fastIdct #-}
--- | Algorithm to call to perform an IDCT, return the same
--- block that the one given as input.
-fastIdct :: MutableMacroBlock s Int16
-         -> ST s (MutableMacroBlock s Int16)
-fastIdct block = rows 0
-  where rows 8 = cols 0
-        rows i = idctRow block (8 * i) >> rows (i + 1)
-
-        cols 8 = return block
-        cols i = idctCol block i >> cols (i + 1)
-
-{-# INLINE mutableLevelShift #-}
--- | Perform a Jpeg level shift in a mutable fashion.
-mutableLevelShift :: MutableMacroBlock s Int16
-                  -> ST s (MutableMacroBlock s Int16)
-mutableLevelShift block = update 0
-  where update 64 = return block
-        update idx = do
-            val <- block `M.unsafeRead` idx
-            (block `M.unsafeWrite` idx) $ val + 128
-            update $ idx + 1
-
+ src/Codec/Picture/Jpg/Internal/Common.hs view
@@ -0,0 +1,240 @@+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE CPP #-}
+module Codec.Picture.Jpg.Internal.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.Internal.Types
+import Codec.Picture.Jpg.Internal.FastIdct
+import Codec.Picture.Jpg.Internal.DefaultTable
+
+-- | Same as for DcCoefficient, to provide nicer type signatures
+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
+      -- | When in progressive mode, we can have many
+      -- color in a scan or only one. The indices changes
+      -- on this fact, when mixed, there is whole 
+      -- MCU for all color components, spanning multiple
+      -- block lines. With only one color component we use
+      -- the normal raster order.
+    , 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) {-  do
+  bits <- replicateM 8 getNextBitJpg
+  if bits == replicate 8 True
+     then do
+         marker <- replicateM 8 getNextBitJpg
+         return $ packInt marker
+     else return (-1)
+        -}
+
+{-# INLINE decodeInt #-}
+decodeInt :: Int -> BoolReader s Int32
+decodeInt ssss = do
+    signBit <- getNextBitJpg
+    let dataRange = 1 `unsafeShiftL` fromIntegral (ssss - 1)
+        leftBitCount = ssss - 1
+    -- First following bits store the sign of the coefficient, and counted in
+    -- SSSS, so the bit count for the int, is 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)
+
+-- | Apply a quantization matrix to a macroblock
+{-# 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
+
+-- | Unpack an int of the given size encoded from MSB to LSB.
+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
+
+-- | Given a size coefficient (how much a pixel span horizontally
+-- and vertically), the position of the macroblock, return a list
+-- of indices and value to be stored in an array (like the final
+-- image)
+unpackMacroBlock :: Int    -- ^ Component count
+                 -> Int -- ^ Width coefficient
+                 -> Int -- ^ Height coefficient
+                 -> Int -- ^ Component index
+                 -> Int -- ^ x
+                 -> Int -- ^ y
+                 -> 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)
+
+-- | This is one of the most important function of the decoding,
+-- it form the barebone decoding pipeline for macroblock. It's all
+-- there is to know for macro block transformation
+decodeMacroBlock :: MacroBlock DctCoefficients
+                 -> MutableMacroBlock s Int16
+                 -> MutableMacroBlock s Int16
+                 -> ST s (MutableMacroBlock s Int16)
+decodeMacroBlock quantizationTable zigZagBlock block =
+    deQuantize quantizationTable block >>= zigZagReorder zigZagBlock
+                                       >>= inverseDirectCosineTransform
+
+ src/Codec/Picture/Jpg/Internal/DefaultTable.hs view
@@ -0,0 +1,294 @@+{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE FlexibleContexts #-}
+-- | Module used by the jpeg decoder internally, shouldn't be used
+-- in user code.
+module Codec.Picture.Jpg.Internal.DefaultTable( DctComponent( .. )
+                                     , HuffmanTree( .. )
+                                     , HuffmanTable
+                                     , HuffmanPackedTree
+                                     , MacroBlock
+                                     , QuantificationTable
+                                     , HuffmanWriterCode 
+                                     , scaleQuantisationMatrix
+                                     , makeMacroBlock
+                                     , makeInverseTable
+                                     , buildHuffmanTree
+                                     , packHuffmanTree
+                                     , huffmanPackedDecode
+
+                                     , defaultChromaQuantizationTable
+
+                                     , defaultLumaQuantizationTable
+
+                                     , defaultAcChromaHuffmanTree
+                                     , defaultAcChromaHuffmanTable
+
+                                     , defaultAcLumaHuffmanTree 
+                                     , defaultAcLumaHuffmanTable 
+
+                                     , defaultDcChromaHuffmanTree 
+                                     , defaultDcChromaHuffmanTable
+
+                                     , defaultDcLumaHuffmanTree
+                                     , defaultDcLumaHuffmanTable
+                                     ) where
+
+import Data.Int( Int16 )
+import Foreign.Storable ( Storable )
+import Control.Monad.ST( runST )
+import qualified Data.Vector.Storable as SV
+import qualified Data.Vector as V
+import Data.Bits( unsafeShiftL, (.|.), (.&.) )
+import Data.Word( Word8, Word16 )
+import Data.List( foldl' )
+import qualified Data.Vector.Storable.Mutable as M
+
+import Codec.Picture.BitWriter
+
+-- | Tree storing the code used for huffman encoding.
+data HuffmanTree = Branch HuffmanTree HuffmanTree -- ^ If bit is 0 take the first subtree, if 1, the right.
+                 | Leaf Word8       -- ^ We should output the value
+                 | Empty            -- ^ no value present
+                 deriving (Eq, Show)
+
+type HuffmanPackedTree = SV.Vector Word16
+
+type HuffmanWriterCode = V.Vector (Word8, Word16)
+
+packHuffmanTree :: HuffmanTree -> HuffmanPackedTree
+packHuffmanTree tree = runST $ do
+    table <- M.replicate 512 0x8000
+    let aux (Empty) idx = return $ idx + 1
+        aux (Leaf v) idx = do
+            (table `M.unsafeWrite` idx) $ fromIntegral v .|. 0x4000
+            return $ idx + 1
+
+        aux (Branch i1@(Leaf _) i2@(Leaf _)) idx =
+            aux i1 idx >>= aux i2
+
+        aux (Branch i1@(Leaf _) i2) idx = do
+            _ <- aux i1 idx
+            ix2 <- aux i2 $ idx + 2
+            (table `M.unsafeWrite` (idx + 1)) $ fromIntegral $ idx + 2
+            return ix2
+
+        aux (Branch i1 i2@(Leaf _)) idx = do
+            ix1 <- aux i1 (idx + 2)
+            _ <- aux i2 (idx + 1)
+            (table `M.unsafeWrite` idx) . fromIntegral $ idx + 2
+            return ix1
+
+        aux (Branch i1 i2) idx = do
+            ix1 <- aux i1 (idx + 2)
+            ix2 <- aux i2 ix1
+            (table `M.unsafeWrite` idx) (fromIntegral $ idx + 2)
+            (table `M.unsafeWrite` (idx + 1)) (fromIntegral ix1)
+            return ix2
+    _ <- aux tree 0
+    SV.unsafeFreeze table
+
+makeInverseTable :: HuffmanTree -> HuffmanWriterCode
+makeInverseTable t = V.replicate 255 (0,0) V.// inner 0 0 t
+  where inner _     _     Empty   = []
+        inner depth code (Leaf v) = [(fromIntegral v, (depth, code))]
+        inner depth code (Branch l r) =
+          inner (depth + 1) shifted l ++ inner (depth + 1) (shifted .|. 1) r
+            where shifted = code `unsafeShiftL` 1
+
+-- | Represent a compact array of 8 * 8 values. The size
+-- is not guarenteed by type system, but if makeMacroBlock is
+-- used, everything should be fine size-wise
+type MacroBlock a = SV.Vector a
+
+type QuantificationTable = MacroBlock Int16
+
+-- | Helper function to create pure macro block of the good size.
+makeMacroBlock :: (Storable a) => [a] -> MacroBlock a
+makeMacroBlock = SV.fromListN 64
+
+-- | Enumeration used to search in the tables for different components.
+data DctComponent = DcComponent | AcComponent
+    deriving (Eq, Show)
+
+-- | Transform parsed coefficients from the jpeg header to a
+-- tree which can be used to decode data.
+buildHuffmanTree :: [[Word8]] -> HuffmanTree
+buildHuffmanTree table = foldl' insertHuffmanVal Empty
+                       . concatMap (\(i, t) -> map (i + 1,) t)
+                       $ zip ([0..] :: [Int]) table
+  where isTreeFullyDefined Empty = False
+        isTreeFullyDefined (Leaf _) = True
+        isTreeFullyDefined (Branch l r) = isTreeFullyDefined l && isTreeFullyDefined r
+
+        insertHuffmanVal Empty (0, val) = Leaf val
+        insertHuffmanVal Empty (d, val) = Branch (insertHuffmanVal Empty (d - 1, val)) Empty
+        insertHuffmanVal (Branch l r) (d, val)
+            | isTreeFullyDefined l = Branch l (insertHuffmanVal r (d - 1, val))
+            | otherwise            = Branch (insertHuffmanVal l (d - 1, val)) r
+        insertHuffmanVal (Leaf _) _ = error "Inserting in value, shouldn't happen"
+
+scaleQuantisationMatrix :: Int -> QuantificationTable -> QuantificationTable 
+scaleQuantisationMatrix quality
+    | quality < 0 = scaleQuantisationMatrix 0
+        -- shouldn't show much difference than with 1,
+        -- but hey, at least we're complete
+    | quality == 0 = SV.map (scale (10000 :: Int))
+    | quality < 50 = let qq = 5000 `div` quality
+                     in SV.map (scale qq)
+    | otherwise    = SV.map (scale q)
+          where q = 200 - quality * 2
+                scale coeff i = fromIntegral . min 255 
+                                             . max 1 
+                                             $ fromIntegral i * coeff `div` 100
+
+huffmanPackedDecode :: HuffmanPackedTree -> BoolReader s Word8
+huffmanPackedDecode table = getNextBitJpg >>= aux 0
+  where aux idx b
+            | (v .&. 0x8000) /= 0 = return  0
+            | (v .&. 0x4000) /= 0 = return . fromIntegral $ v .&. 0xFF
+            | otherwise = getNextBitJpg >>= aux v
+          where tableIndex | b = idx + 1
+                           | otherwise = idx
+                v = table `SV.unsafeIndex` fromIntegral tableIndex
+
+defaultLumaQuantizationTable :: QuantificationTable
+defaultLumaQuantizationTable = makeMacroBlock
+    [16, 11, 10, 16,  24,  40,  51,  61
+    ,12, 12, 14, 19,  26,  58,  60,  55
+    ,14, 13, 16, 24,  40,  57,  69,  56
+    ,14, 17, 22, 29,  51,  87,  80,  62
+    ,18, 22, 37, 56,  68, 109, 103,  77
+    ,24, 35, 55, 64,  81, 104, 113,  92
+    ,49, 64, 78, 87, 103, 121, 120, 101
+    ,72, 92, 95, 98, 112, 100, 103,  99
+    ]
+
+defaultChromaQuantizationTable :: QuantificationTable
+defaultChromaQuantizationTable = makeMacroBlock
+    [17, 18, 24, 47, 99, 99, 99, 99
+    ,18, 21, 26, 66, 99, 99, 99, 99
+    ,24, 26, 56, 99, 99, 99, 99, 99
+    ,47, 66, 99, 99, 99, 99, 99, 99
+    ,99, 99, 99, 99, 99, 99, 99, 99
+    ,99, 99, 99, 99, 99, 99, 99, 99
+    ,99, 99, 99, 99, 99, 99, 99, 99
+    ,99, 99, 99, 99, 99, 99, 99, 99
+    ]
+
+defaultDcLumaHuffmanTree :: HuffmanTree
+defaultDcLumaHuffmanTree = buildHuffmanTree defaultDcLumaHuffmanTable
+
+-- | From the Table K.3 of ITU-81 (p153)
+defaultDcLumaHuffmanTable :: HuffmanTable
+defaultDcLumaHuffmanTable =
+    [ []
+    , [0]
+    , [1, 2, 3, 4, 5]
+    , [6]
+    , [7]
+    , [8]
+    , [9]
+    , [10]
+    , [11]
+    , []
+    , []
+    , []
+    , []
+    , []
+    , []
+    , []
+    ]
+
+defaultDcChromaHuffmanTree :: HuffmanTree
+defaultDcChromaHuffmanTree = buildHuffmanTree defaultDcChromaHuffmanTable
+
+-- | From the Table K.4 of ITU-81 (p153)
+defaultDcChromaHuffmanTable :: HuffmanTable
+defaultDcChromaHuffmanTable = 
+    [ []
+    , [0, 1, 2]
+    , [3]
+    , [4]
+    , [5]
+    , [6]
+    , [7]
+    , [8]
+    , [9]
+    , [10]
+    , [11]
+    , []
+    , []
+    , []
+    , []
+    , []
+    ]
+
+defaultAcLumaHuffmanTree :: HuffmanTree
+defaultAcLumaHuffmanTree = buildHuffmanTree defaultAcLumaHuffmanTable
+
+-- | From the Table K.5 of ITU-81 (p154)
+defaultAcLumaHuffmanTable :: HuffmanTable
+defaultAcLumaHuffmanTable =
+    [ []
+    , [0x01, 0x02]
+    , [0x03]
+    , [0x00, 0x04, 0x11]
+    , [0x05, 0x12, 0x21]
+    , [0x31, 0x41]
+    , [0x06, 0x13, 0x51, 0x61]
+    , [0x07, 0x22, 0x71]
+    , [0x14, 0x32, 0x81, 0x91, 0xA1]
+    , [0x08, 0x23, 0x42, 0xB1, 0xC1]
+    , [0x15, 0x52, 0xD1, 0xF0]
+    , [0x24, 0x33, 0x62, 0x72]
+    , []
+    , []
+    , [0x82]
+    , [0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35
+      ,0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54
+      ,0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73
+      ,0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A
+      ,0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7
+      ,0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4
+      ,0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA
+      ,0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5
+      ,0xF6, 0xF7, 0xF8, 0xF9, 0xFA]
+    ]
+
+type HuffmanTable = [[Word8]]
+
+defaultAcChromaHuffmanTree :: HuffmanTree
+defaultAcChromaHuffmanTree = buildHuffmanTree defaultAcChromaHuffmanTable 
+
+defaultAcChromaHuffmanTable :: HuffmanTable
+defaultAcChromaHuffmanTable = 
+    [ []
+    , [0x00, 0x01]
+    , [0x02]
+    , [0x03, 0x11]
+    , [0x04, 0x05, 0x21, 0x31]
+    , [0x06, 0x12, 0x41, 0x51]
+    , [0x07, 0x61, 0x71]
+    , [0x13, 0x22, 0x32, 0x81]
+    , [0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1]
+    , [0x09, 0x23, 0x33, 0x52, 0xF0]
+    , [0x15, 0x62, 0x72, 0xD1]
+    , [0x0A, 0x16, 0x24, 0x34]
+    , []
+    , [0xE1]
+    , [0x25, 0xF1]
+    , [ 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35
+      , 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47
+      , 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59
+      , 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73
+      , 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84
+      , 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95
+      , 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6
+      , 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7
+      , 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8
+      , 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9
+      , 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA
+      , 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA
+      ]
+    ]
+
+ src/Codec/Picture/Jpg/Internal/FastDct.hs view
@@ -0,0 +1,218 @@+{-# LANGUAGE CPP #-}
+{-# LANGUAGE TypeFamilies #-}
+module Codec.Picture.Jpg.Internal.FastDct( referenceDct, fastDctLibJpeg ) where
+
+#if !MIN_VERSION_base(4,8,0)
+import Control.Applicative( (<$>) )
+#endif
+
+import Data.Int( Int16, Int32 )
+import Data.Bits( unsafeShiftR, unsafeShiftL )
+import Control.Monad.ST( ST )
+
+import qualified Data.Vector.Storable.Mutable as M
+
+import Codec.Picture.Jpg.Internal.Types
+import Control.Monad( forM, forM_ )
+
+-- | Reference implementation of the DCT, directly implementing the formula
+-- of ITU-81. It's slow as hell, perform to many operations, but is accurate
+-- and a good reference point.
+referenceDct :: MutableMacroBlock s Int32
+             -> MutableMacroBlock s Int16
+             -> ST s (MutableMacroBlock s Int32)
+referenceDct workData block = do
+  forM_ [(u, v) | u <- [0 :: Int .. dctBlockSize - 1], v <- [0..dctBlockSize - 1]] $ \(u,v) -> do
+    val <- at (u,v)
+    (workData `M.unsafeWrite` (v * dctBlockSize + u)) . truncate $ (1 / 4) * c u * c v * val
+
+  return workData
+ where -- at :: (Int, Int) -> ST s Float
+   at (u,v) = do
+     toSum <-
+        forM [(x,y) | x <- [0..dctBlockSize - 1], y <- [0..dctBlockSize - 1 :: Int]] $ \(x,y) -> do
+            sample <- fromIntegral <$> (block `M.unsafeRead` (y * dctBlockSize + x))
+            return $ sample * cos ((2 * fromIntegral x + 1) * fromIntegral u * (pi :: Float)/ 16) 
+                            * cos ((2 * fromIntegral y + 1) * fromIntegral v * pi / 16)
+     return $ sum toSum
+
+   c 0 = 1 / sqrt 2
+   c _ = 1
+
+pASS1_BITS, cONST_BITS :: Int
+cONST_BITS = 13
+pASS1_BITS =  2
+
+
+fIX_0_298631336, fIX_0_390180644, fIX_0_541196100,
+    fIX_0_765366865, fIX_0_899976223, fIX_1_175875602,
+    fIX_1_501321110, fIX_1_847759065, fIX_1_961570560,
+    fIX_2_053119869, fIX_2_562915447, fIX_3_072711026 :: Int32
+fIX_0_298631336 = 2446    -- FIX(0.298631336) */
+fIX_0_390180644 = 3196    -- FIX(0.390180644) */
+fIX_0_541196100 = 4433    -- FIX(0.541196100) */
+fIX_0_765366865 = 6270    -- FIX(0.765366865) */
+fIX_0_899976223 = 7373    -- FIX(0.899976223) */
+fIX_1_175875602 = 9633    -- FIX(1.175875602) */
+fIX_1_501321110 = 12299    -- FIX(1.501321110) */
+fIX_1_847759065 = 15137    -- FIX(1.847759065) */
+fIX_1_961570560 = 16069    -- FIX(1.961570560) */
+fIX_2_053119869 = 16819    -- FIX(2.053119869) */
+fIX_2_562915447 = 20995    -- FIX(2.562915447) */
+fIX_3_072711026 = 25172    -- FIX(3.072711026) */
+
+cENTERJSAMPLE :: Int32
+cENTERJSAMPLE = 128
+
+-- | Fast DCT extracted from libjpeg
+fastDctLibJpeg :: MutableMacroBlock s Int32
+        -> MutableMacroBlock s Int16
+        -> ST s (MutableMacroBlock s Int32)
+fastDctLibJpeg workData sample_block = do
+ firstPass workData  0
+ secondPass workData 7
+ {-_ <- mutate (\_ a -> a `quot` 8) workData-}
+ return workData
+  where -- Pass 1: process rows.
+        -- Note results are scaled up by sqrt(8) compared to a true DCT;
+        -- furthermore, we scale the results by 2**PASS1_BITS.
+        firstPass         _ i | i == dctBlockSize = return ()
+        firstPass dataBlock i = do
+            let baseIdx = i * dctBlockSize
+                readAt idx = fromIntegral <$> sample_block `M.unsafeRead` (baseIdx + idx)
+                mult = (*)
+                writeAt idx = dataBlock `M.unsafeWrite` (baseIdx + idx)
+                writeAtPos idx n = (dataBlock `M.unsafeWrite` (baseIdx + idx))
+                                    (n `unsafeShiftR` (cONST_BITS - pASS1_BITS))
+
+            blk0 <- readAt 0
+            blk1 <- readAt 1
+            blk2 <- readAt 2
+            blk3 <- readAt 3
+            blk4 <- readAt 4
+            blk5 <- readAt 5
+            blk6 <- readAt 6
+            blk7 <- readAt 7
+
+            let tmp0 = blk0 + blk7
+                tmp1 = blk1 + blk6
+                tmp2 = blk2 + blk5
+                tmp3 = blk3 + blk4
+
+                tmp10 = tmp0 + tmp3
+                tmp12 = tmp0 - tmp3
+                tmp11 = tmp1 + tmp2
+                tmp13 = tmp1 - tmp2
+
+                tmp0' = blk0 - blk7
+                tmp1' = blk1 - blk6
+                tmp2' = blk2 - blk5
+                tmp3' = blk3 - blk4
+
+            -- Stage 4 and output
+            writeAt 0 $ (tmp10 + tmp11 - dctBlockSize * cENTERJSAMPLE) `unsafeShiftL` pASS1_BITS
+            writeAt 4 $ (tmp10 - tmp11) `unsafeShiftL` pASS1_BITS
+
+            let z1 = mult (tmp12 + tmp13) fIX_0_541196100
+                     + (1 `unsafeShiftL` (cONST_BITS - pASS1_BITS - 1))
+
+            writeAtPos 2 $ z1 + mult tmp12 fIX_0_765366865
+            writeAtPos 6 $ z1 - mult tmp13 fIX_1_847759065
+
+            let tmp10' = tmp0' + tmp3'
+                tmp11' = tmp1' + tmp2'
+                tmp12' = tmp0' + tmp2'
+                tmp13' = tmp1' + tmp3'
+                z1' = mult (tmp12' + tmp13') fIX_1_175875602 --  c3 */
+                        -- Add fudge factor here for final descale. */
+                        + (1 `unsafeShiftL` (cONST_BITS - pASS1_BITS-1))
+                tmp0'' = mult tmp0' fIX_1_501321110
+                tmp1'' = mult tmp1' fIX_3_072711026
+                tmp2'' = mult tmp2' fIX_2_053119869
+                tmp3'' = mult tmp3' fIX_0_298631336
+
+                tmp10'' = mult tmp10' (- fIX_0_899976223)
+                tmp11'' = mult tmp11' (- fIX_2_562915447)
+                tmp12'' = mult tmp12' (- fIX_0_390180644) + z1'
+                tmp13'' = mult tmp13' (- fIX_1_961570560) + z1'
+
+            writeAtPos 1 $ tmp0'' + tmp10'' + tmp12''
+            writeAtPos 3 $ tmp1'' + tmp11'' + tmp13''
+            writeAtPos 5 $ tmp2'' + tmp11'' + tmp12''
+            writeAtPos 7 $ tmp3'' + tmp10'' + tmp13''
+
+            firstPass dataBlock $ i + 1
+
+        -- Pass 2: process columns.
+        -- We remove the PASS1_BITS scaling, but leave the results scaled up
+        -- by an overall factor of 8.
+        secondPass :: M.STVector s Int32 -> Int -> ST s ()
+        secondPass _     (-1) = return ()
+        secondPass block i = do
+            let readAt idx = block `M.unsafeRead` ((7 - i) + idx * dctBlockSize)
+                mult = (*)
+                writeAt idx = block `M.unsafeWrite` (dctBlockSize * idx + (7 - i))
+                writeAtPos idx n = (block `M.unsafeWrite` (dctBlockSize * idx + (7 - i))) $ n `unsafeShiftR` (cONST_BITS + pASS1_BITS + 3)
+            blk0 <- readAt 0
+            blk1 <- readAt 1
+            blk2 <- readAt 2
+            blk3 <- readAt 3
+            blk4 <- readAt 4
+            blk5 <- readAt 5
+            blk6 <- readAt 6
+            blk7 <- readAt 7
+
+            let tmp0 = blk0 + blk7
+                tmp1 = blk1 + blk6
+                tmp2 = blk2 + blk5
+                tmp3 = blk3 + blk4
+
+                -- Add fudge factor here for final descale. */
+                tmp10 = tmp0 + tmp3 + (1 `unsafeShiftL` (pASS1_BITS-1))
+                tmp12 = tmp0 - tmp3
+                tmp11 = tmp1 + tmp2
+                tmp13 = tmp1 - tmp2
+
+                tmp0' = blk0 - blk7
+                tmp1' = blk1 - blk6
+                tmp2' = blk2 - blk5
+                tmp3' = blk3 - blk4
+
+            writeAt 0 $ (tmp10 + tmp11) `unsafeShiftR` (pASS1_BITS + 3)
+            writeAt 4 $ (tmp10 - tmp11) `unsafeShiftR` (pASS1_BITS + 3)
+
+            let z1 = mult (tmp12 + tmp13) fIX_0_541196100
+                    + (1 `unsafeShiftL` (cONST_BITS + pASS1_BITS - 1))
+
+            writeAtPos 2 $ z1 + mult tmp12 fIX_0_765366865
+            writeAtPos 6 $ z1 - mult tmp13 fIX_1_847759065
+
+            let tmp10' = tmp0' + tmp3'
+                tmp11' = tmp1' + tmp2'
+                tmp12' = tmp0' + tmp2'
+                tmp13' = tmp1' + tmp3'
+
+                z1' = mult (tmp12' + tmp13') fIX_1_175875602
+                    -- Add fudge factor here for final descale. */
+                   + 1 `unsafeShiftL` (cONST_BITS+pASS1_BITS-1);
+
+                tmp0''  = mult tmp0'    fIX_1_501321110
+                tmp1''  = mult tmp1'    fIX_3_072711026
+                tmp2''  = mult tmp2'    fIX_2_053119869
+                tmp3''  = mult tmp3'    fIX_0_298631336
+                tmp10'' = mult tmp10' (- fIX_0_899976223)
+                tmp11'' = mult tmp11' (- fIX_2_562915447)
+                tmp12'' = mult tmp12' (- fIX_0_390180644)
+                            + z1'
+                tmp13'' = mult tmp13' (- fIX_1_961570560)
+                            + z1'
+            writeAtPos 1 $ tmp0'' + tmp10'' + tmp12''
+            writeAtPos 3 $ tmp1'' + tmp11'' + tmp13''
+            writeAtPos 5 $ tmp2'' + tmp11'' + tmp12''
+            writeAtPos 7 $ tmp3'' + tmp10'' + tmp13''
+
+            secondPass block (i - 1)
+
+{-# ANN module "HLint: ignore Use camelCase" #-}
+{-# ANN module "HLint: ignore Reduce duplication" #-}
+
+ src/Codec/Picture/Jpg/Internal/FastIdct.hs view
@@ -0,0 +1,229 @@+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE TypeFamilies #-}
+-- | Module providing a 'fast' implementation of IDCT
+--
+-- inverse two dimensional DCT, Chen-Wang algorithm       
+-- (cf. IEEE ASSP-32, pp. 803-816, Aug. 1984)             
+-- 32-bit integer arithmetic (8 bit coefficients)         
+-- 11 mults, 29 adds per DCT                              
+--                                      sE, 18.8.91       
+--
+-- coefficients extended to 12 bit for IEEE1180-1990      
+-- compliance                           sE,  2.1.94       
+--
+-- this code assumes >> to be a two's-complement arithmetic
+-- right shift: (-2)>>1 == -1 , (-3)>>1 == -2               
+module Codec.Picture.Jpg.Internal.FastIdct( MutableMacroBlock
+                                 , fastIdct
+                                 , mutableLevelShift
+                                 , createEmptyMutableMacroBlock
+                                 ) where
+
+import qualified Data.Vector.Storable as V
+import Control.Monad.ST( ST )
+import Data.Bits( unsafeShiftL, unsafeShiftR )
+import Data.Int( Int16 )
+import qualified Data.Vector.Storable.Mutable as M
+
+import Codec.Picture.Jpg.Internal.Types
+
+iclip :: V.Vector Int16
+iclip = V.fromListN 1024 [ val i| i <- [(-512) .. 511] ]
+    where val i | i < (-256) = -256
+                | i > 255    =  255
+                | otherwise  =  i
+
+data IDctStage = IDctStage { 
+        x0 :: {-# UNPACK #-} !Int,
+        x1 :: {-# UNPACK #-} !Int,
+        x2 :: {-# UNPACK #-} !Int,
+        x3 :: {-# UNPACK #-} !Int,
+        x4 :: {-# UNPACK #-} !Int,
+        x5 :: {-# UNPACK #-} !Int,
+        x6 :: {-# UNPACK #-} !Int,
+        x7 :: {-# UNPACK #-} !Int,
+        x8 :: {-# UNPACK #-} !Int
+    }
+
+w1, w2, w3, w5, w6, w7 :: Int
+w1 = 2841 -- 2048*sqrt(2)*cos(1*pi/16)
+w2 = 2676 -- 2048*sqrt(2)*cos(2*pi/16)
+w3 = 2408 -- 2048*sqrt(2)*cos(3*pi/16)
+w5 = 1609 -- 2048*sqrt(2)*cos(5*pi/16)
+w6 = 1108 -- 2048*sqrt(2)*cos(6*pi/16)
+w7 = 565  -- 2048*sqrt(2)*cos(7*pi/16)
+
+-- row (horizontal) IDCT
+--
+--           7                       pi         1
+-- dst[k] = sum c[l] * src[l] * cos( -- * ( k + - ) * l )
+--          l=0                      8          2
+--
+-- where: c[0]    = 128
+--        c[1..7] = 128*sqrt(2)
+idctRow :: MutableMacroBlock s Int16 -> Int ->  ST s ()
+idctRow blk idx = do
+  xx0 <- blk `M.unsafeRead` (0 + idx)
+  xx1 <- blk `M.unsafeRead` (4 + idx) 
+  xx2 <- blk `M.unsafeRead` (6 + idx)
+  xx3 <- blk `M.unsafeRead` (2 + idx)
+  xx4 <- blk `M.unsafeRead` (1 + idx)
+  xx5 <- blk `M.unsafeRead` (7 + idx)
+  xx6 <- blk `M.unsafeRead` (5 + idx)
+  xx7 <- blk `M.unsafeRead` (3 + idx)
+  let initialState = IDctStage { x0 = (fromIntegral xx0 `unsafeShiftL` 11) + 128
+                               , x1 =  fromIntegral xx1 `unsafeShiftL` 11
+                               , x2 =  fromIntegral xx2
+                               , x3 =  fromIntegral xx3
+                               , x4 =  fromIntegral xx4
+                               , x5 =  fromIntegral xx5
+                               , x6 =  fromIntegral xx6
+                               , x7 =  fromIntegral xx7
+                               , x8 = 0
+                               } 
+
+      firstStage c = c { x4 = x8'  + (w1 - w7) * x4 c
+                       , x5 = x8'  - (w1 + w7) * x5 c
+                       , x6 = x8'' - (w3 - w5) * x6 c
+                       , x7 = x8'' - (w3 + w5) * x7 c
+                       , x8 = x8''
+                       }
+          where x8' = w7 * (x4 c + x5 c)
+                x8'' = w3 * (x6 c + x7 c)
+
+      secondStage c = c { x0 = x0 c - x1 c
+                        , x8 = x0 c + x1 c 
+                        , x1 = x1''
+                        , x2 = x1' - (w2 + w6) * x2 c
+                        , x3 = x1' + (w2 - w6) * x3 c
+                        , x4 = x4 c - x6 c
+                        , x6 = x5 c + x7 c
+                        , x5 = x5 c - x7 c
+                        }
+            where x1'  = w6 * (x3 c + x2 c)
+                  x1'' = x4 c + x6 c
+
+      thirdStage c = c { x7 = x8 c + x3 c
+                       , x8 = x8 c - x3 c
+                       , x3 = x0 c + x2 c
+                       , x0 = x0 c - x2 c
+                       , x2 = (181 * (x4 c + x5 c) + 128) `unsafeShiftR` 8
+                       , x4 = (181 * (x4 c - x5 c) + 128) `unsafeShiftR` 8
+                       }
+      scaled c = c { x0 = (x7 c + x1 c) `unsafeShiftR` 8
+                   , x1 = (x3 c + x2 c) `unsafeShiftR` 8
+                   , x2 = (x0 c + x4 c) `unsafeShiftR` 8
+                   , x3 = (x8 c + x6 c) `unsafeShiftR` 8
+                   , x4 = (x8 c - x6 c) `unsafeShiftR` 8
+                   , x5 = (x0 c - x4 c) `unsafeShiftR` 8
+                   , x6 = (x3 c - x2 c) `unsafeShiftR` 8
+                   , x7 = (x7 c - x1 c) `unsafeShiftR` 8
+                   }
+      transformed = scaled . thirdStage . secondStage $ firstStage initialState
+
+  (blk `M.unsafeWrite` (0 + idx)) . fromIntegral $ x0 transformed
+  (blk `M.unsafeWrite` (1 + idx)) . fromIntegral $ x1 transformed
+  (blk `M.unsafeWrite` (2 + idx)) . fromIntegral $ x2 transformed
+  (blk `M.unsafeWrite` (3 + idx)) . fromIntegral $ x3 transformed
+  (blk `M.unsafeWrite` (4 + idx)) . fromIntegral $ x4 transformed
+  (blk `M.unsafeWrite` (5 + idx)) . fromIntegral $ x5 transformed
+  (blk `M.unsafeWrite` (6 + idx)) . fromIntegral $ x6 transformed
+  (blk `M.unsafeWrite` (7 + idx)) . fromIntegral $ x7 transformed
+
+-- column (vertical) IDCT
+--
+--             7                         pi         1
+-- dst[8*k] = sum c[l] * src[8*l] * cos( -- * ( k + - ) * l )
+--            l=0                        8          2
+--
+-- where: c[0]    = 1/1024
+--        c[1..7] = (1/1024)*sqrt(2)
+--
+idctCol :: MutableMacroBlock s Int16 -> Int -> ST s ()
+idctCol blk idx = do
+  xx0 <- blk `M.unsafeRead` (    0 + idx)
+  xx1 <- blk `M.unsafeRead` (8 * 4 + idx)
+  xx2 <- blk `M.unsafeRead` (8 * 6 + idx)
+  xx3 <- blk `M.unsafeRead` (8 * 2 + idx)
+  xx4 <- blk `M.unsafeRead` (8     + idx)
+  xx5 <- blk `M.unsafeRead` (8 * 7 + idx)
+  xx6 <- blk `M.unsafeRead` (8 * 5 + idx)
+  xx7 <- blk `M.unsafeRead` (8 * 3 + idx)
+  let initialState = IDctStage { x0 = (fromIntegral xx0 `unsafeShiftL` 8) + 8192
+                               , x1 =  fromIntegral xx1 `unsafeShiftL` 8
+                               , x2 =  fromIntegral xx2
+                               , x3 =  fromIntegral xx3
+                               , x4 =  fromIntegral xx4
+                               , x5 =  fromIntegral xx5
+                               , x6 =  fromIntegral xx6
+                               , x7 =  fromIntegral xx7
+                               , x8 = 0
+                               } 
+      firstStage c = c { x4 = (x8'  + (w1 - w7) * x4 c) `unsafeShiftR` 3
+                       , x5 = (x8'  - (w1 + w7) * x5 c) `unsafeShiftR` 3
+                       , x6 = (x8'' - (w3 - w5) * x6 c) `unsafeShiftR` 3
+                       , x7 = (x8'' - (w3 + w5) * x7 c) `unsafeShiftR` 3
+                       , x8 = x8''
+                       }
+          where x8'  = w7 * (x4 c + x5 c) + 4
+                x8'' = w3 * (x6 c + x7 c) + 4
+
+      secondStage c = c { x8 = x0 c + x1 c
+                        , x0 = x0 c - x1 c
+                        , x2 = (x1' - (w2 + w6) * x2 c) `unsafeShiftR` 3
+                        , x3 = (x1' + (w2 - w6) * x3 c) `unsafeShiftR` 3
+                        , x4 = x4 c - x6 c
+                        , x1 = x1''
+                        , x6 = x5 c + x7 c
+                        , x5 = x5 c - x7 c
+                        }
+          where x1'  = w6 * (x3 c + x2 c) + 4
+                x1'' = x4 c + x6 c
+  
+      thirdStage c = c { x7 = x8 c + x3 c
+                       , x8 = x8 c - x3 c
+                       , x3 = x0 c + x2 c
+                       , x0 = x0 c - x2 c
+                       , x2 = (181 * (x4 c + x5 c) + 128) `unsafeShiftR` 8
+                       , x4 = (181 * (x4 c - x5 c) + 128) `unsafeShiftR` 8
+                       }
+
+      clip i | i < 511 = if i > -512 then iclip `V.unsafeIndex` (i + 512)
+                                    else iclip `V.unsafeIndex` 0
+            
+             | otherwise = iclip `V.unsafeIndex` 1023
+
+      f = thirdStage . secondStage $ firstStage initialState
+  (blk `M.unsafeWrite` (idx + 8*0)) . clip $ (x7 f + x1 f) `unsafeShiftR` 14
+  (blk `M.unsafeWrite` (idx + 8  )) . clip $ (x3 f + x2 f) `unsafeShiftR` 14
+  (blk `M.unsafeWrite` (idx + 8*2)) . clip $ (x0 f + x4 f) `unsafeShiftR` 14
+  (blk `M.unsafeWrite` (idx + 8*3)) . clip $ (x8 f + x6 f) `unsafeShiftR` 14
+  (blk `M.unsafeWrite` (idx + 8*4)) . clip $ (x8 f - x6 f) `unsafeShiftR` 14
+  (blk `M.unsafeWrite` (idx + 8*5)) . clip $ (x0 f - x4 f) `unsafeShiftR` 14
+  (blk `M.unsafeWrite` (idx + 8*6)) . clip $ (x3 f - x2 f) `unsafeShiftR` 14
+  (blk `M.unsafeWrite` (idx + 8*7)) . clip $ (x7 f - x1 f) `unsafeShiftR` 14
+
+
+{-# INLINE fastIdct #-}
+-- | Algorithm to call to perform an IDCT, return the same
+-- block that the one given as input.
+fastIdct :: MutableMacroBlock s Int16
+         -> ST s (MutableMacroBlock s Int16)
+fastIdct block = rows 0
+  where rows 8 = cols 0
+        rows i = idctRow block (8 * i) >> rows (i + 1)
+
+        cols 8 = return block
+        cols i = idctCol block i >> cols (i + 1)
+
+{-# INLINE mutableLevelShift #-}
+-- | Perform a Jpeg level shift in a mutable fashion.
+mutableLevelShift :: MutableMacroBlock s Int16
+                  -> ST s (MutableMacroBlock s Int16)
+mutableLevelShift block = update 0
+  where update 64 = return block
+        update idx = do
+            val <- block `M.unsafeRead` idx
+            (block `M.unsafeWrite` idx) $ val + 128
+            update $ idx + 1
+
+ src/Codec/Picture/Jpg/Internal/Metadata.hs view
@@ -0,0 +1,41 @@+{-# LANGUAGE CPP #-}
+module Codec.Picture.Jpg.Internal.Metadata ( extractMetadatas, encodeMetadatas ) where
+
+#if !MIN_VERSION_base(4,8,0)
+import Control.Applicative( pure )
+import Data.Monoid( mempty )
+import Data.Word( Word )
+#endif
+
+import Data.Word( Word16 )
+import Data.Maybe( fromMaybe )
+import qualified Codec.Picture.Metadata as Met
+import Codec.Picture.Metadata( Metadatas )
+import Codec.Picture.Jpg.Internal.Types
+
+scalerOfUnit :: JFifUnit -> Met.Keys Word -> Word16 -> Metadatas -> Metadatas
+scalerOfUnit unit k v = case unit of
+  JFifUnitUnknown -> id
+  JFifPixelsPerInch -> Met.insert k (fromIntegral v)
+  JFifPixelsPerCentimeter ->
+    Met.insert k (Met.dotsPerCentiMeterToDotPerInch $ fromIntegral v)
+
+extractMetadatas :: JpgJFIFApp0 -> Metadatas
+extractMetadatas jfif = 
+    inserter Met.DpiX (_jfifDpiX jfif)
+        $ inserter Met.DpiY (_jfifDpiY jfif) mempty
+  where
+    inserter = scalerOfUnit $ _jfifUnit jfif
+
+
+encodeMetadatas :: Metadatas -> [JpgFrame]
+encodeMetadatas metas = fromMaybe [] $ do
+  dpiX <- Met.lookup Met.DpiX metas
+  dpiY <- Met.lookup Met.DpiY metas
+  pure . pure . JpgJFIF $ JpgJFIFApp0
+    { _jfifUnit      = JFifPixelsPerInch
+    , _jfifDpiX      = fromIntegral dpiX
+    , _jfifDpiY      = fromIntegral dpiY
+    , _jfifThumbnail = Nothing
+    }
+
+ src/Codec/Picture/Jpg/Internal/Progressive.hs view
@@ -0,0 +1,332 @@+{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE TypeSynonymInstances #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE CPP #-}
+module Codec.Picture.Jpg.Internal.Progressive
+    ( JpgUnpackerParameter( .. )
+    , progressiveUnpack
+    ) where
+
+#if !MIN_VERSION_base(4,8,0)
+import Control.Applicative( pure, (<$>) )
+#endif
+
+import Control.Monad( when, unless, forM_ )
+import Control.Monad.ST( ST )
+import Control.Monad.Trans( lift )
+import Data.Bits( (.&.), (.|.), unsafeShiftL )
+import Data.Int( Int16, Int32 )
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as L
+import qualified Data.Vector as V
+import qualified Data.Vector.Storable as VS
+import Data.Vector( (!) )
+import qualified Data.Vector.Mutable as M
+import qualified Data.Vector.Storable.Mutable as MS
+
+import Codec.Picture.Types
+import Codec.Picture.BitWriter
+import Codec.Picture.Jpg.Internal.Common
+import Codec.Picture.Jpg.Internal.Types
+import Codec.Picture.Jpg.Internal.DefaultTable
+
+createMcuLineIndices :: JpgComponent -> Int -> Int -> V.Vector (VS.Vector Int)
+createMcuLineIndices param imgWidth mcuWidth =
+ V.fromList $ VS.fromList <$> [indexSolo, indexMulti]
+  where compW = fromIntegral $ horizontalSamplingFactor param
+        compH = fromIntegral $ verticalSamplingFactor param
+        imageBlockSize = toBlockSize imgWidth
+
+        -- if the displayed MCU block is only displayed in half (like with
+        -- width 500 then we loose one macroblock of the MCU at the end of
+        -- the line. Previous implementation which naively used full mcu
+        -- was wrong. Only taking into account visible macroblocks
+        indexSolo = [base + x
+            | y <- [0 .. compH - 1]
+            , let base = y * mcuWidth * compW
+            , x <- [0 .. imageBlockSize - 1]]
+
+        indexMulti =
+            [(mcu + y * mcuWidth) * compW + x
+                | mcu <- [0 .. mcuWidth - 1]
+                , y <- [0 .. compH - 1]
+                , x <- [0 .. compW - 1] ]
+
+decodeFirstDC :: JpgUnpackerParameter
+              -> MS.STVector s Int16
+              -> MutableMacroBlock s Int16
+              -> Int32
+              -> BoolReader s Int32
+decodeFirstDC params dcCoeffs block eobrun = unpack >> pure eobrun
+  where unpack = do
+          (dcDeltaCoefficient) <- dcCoefficientDecode $ dcHuffmanTree params
+          previousDc <- lift $ dcCoeffs `MS.unsafeRead` componentIndex params
+          let neoDcCoefficient = previousDc + dcDeltaCoefficient
+              approxLow = fst $ successiveApprox params
+              scaledDc = neoDcCoefficient `unsafeShiftL` approxLow
+          lift $ (block `MS.unsafeWrite` 0) scaledDc 
+          lift $ (dcCoeffs `MS.unsafeWrite` componentIndex params) neoDcCoefficient
+
+decodeRefineDc :: JpgUnpackerParameter
+               -> a
+               -> MutableMacroBlock s Int16
+               -> Int32
+               -> BoolReader s Int32
+decodeRefineDc params _ block eobrun = unpack >> pure eobrun
+  where approxLow = fst $ successiveApprox params
+        plusOne = 1 `unsafeShiftL` approxLow
+        unpack = do
+            bit <- getNextBitJpg
+            when bit . lift $ do
+                v <- block `MS.unsafeRead` 0
+                (block `MS.unsafeWrite` 0) $ v .|. plusOne
+
+decodeFirstAc :: JpgUnpackerParameter
+              -> a
+              -> MutableMacroBlock s Int16
+              -> Int32
+              -> BoolReader s Int32
+decodeFirstAc _params _ _block eobrun | eobrun > 0 = pure $ eobrun - 1
+decodeFirstAc params _ block _ = unpack startIndex
+  where (startIndex, maxIndex) = coefficientRange params
+        (low, _) = successiveApprox params
+        unpack n | n > maxIndex = pure 0
+        unpack n = do
+            rrrrssss <- decodeRrrrSsss $ acHuffmanTree params
+            case rrrrssss of
+                (0xF, 0) -> unpack $ n + 16
+                (  0, 0) -> return 0
+                (  r, 0) -> eobrun <$> unpackInt r
+                    where eobrun lowBits = (1 `unsafeShiftL` r) - 1 + lowBits
+                (  r, s) -> do
+                    let n' = n + r
+                    val <- (`unsafeShiftL` low) <$> decodeInt s
+                    lift . (block `MS.unsafeWrite` n') $ fromIntegral val
+                    unpack $ n' + 1
+
+decodeRefineAc :: forall a s. JpgUnpackerParameter
+               -> a
+               -> MutableMacroBlock s Int16
+               -> Int32
+               -> BoolReader s Int32
+decodeRefineAc params _ block eobrun
+        | eobrun == 0 = unpack startIndex
+        | otherwise   = performEobRun startIndex >> return (eobrun - 1)
+  where (startIndex, maxIndex) = coefficientRange params
+        (low, _) = successiveApprox params
+        plusOne = 1 `unsafeShiftL` low
+        minusOne = (-1) `unsafeShiftL` low
+
+        getBitVal = do
+            v <- getNextBitJpg
+            pure $ if v then plusOne else minusOne
+
+        performEobRun idx | idx > maxIndex = pure ()
+        performEobRun idx = do
+          coeff <- lift $ block `MS.unsafeRead` idx
+          if coeff /= 0 then do
+            bit <- getNextBitJpg
+            case (bit, (coeff .&. plusOne) == 0) of
+               (False, _)    -> performEobRun $ idx + 1
+               (True, False) -> performEobRun $ idx + 1
+               (True, True) -> do
+                   let newVal | coeff >= 0 = coeff + plusOne
+                              | otherwise = coeff + minusOne
+                   lift $ (block `MS.unsafeWrite` idx) newVal
+                   performEobRun $ idx + 1
+          else
+            performEobRun $ idx + 1
+
+        unpack idx | idx > maxIndex = pure 0
+        unpack idx = do
+            rrrrssss <- decodeRrrrSsss $ acHuffmanTree params
+            case rrrrssss of
+              (0xF, 0) -> do
+                idx' <- updateCoeffs 0xF idx
+                unpack $ idx' + 1
+
+              (  r, 0) -> do
+                  lowBits <- unpackInt r
+                  let newEobRun = (1 `unsafeShiftL` r) + lowBits - 1
+                  performEobRun idx
+                  pure newEobRun
+
+              (  r, _) -> do
+                  val <- getBitVal
+                  idx' <- updateCoeffs (fromIntegral r) idx
+                  when (idx' <= maxIndex) $
+                       lift $ (block `MS.unsafeWrite` idx') val
+                  unpack $ idx' + 1
+
+        updateCoeffs :: Int -> Int -> BoolReader s Int
+        updateCoeffs r idx
+            | r   < 0        = pure $ idx - 1
+            | idx > maxIndex = pure idx
+        updateCoeffs r idx = do
+          coeff <- lift $ block `MS.unsafeRead` idx
+          if coeff /= 0 then do
+            bit <- getNextBitJpg
+            when (bit && coeff .&. plusOne == 0) $ do
+              let writeCoeff | coeff >= 0 = coeff + plusOne
+                             | otherwise = coeff + minusOne
+              lift $ (block `MS.unsafeWrite` idx) writeCoeff
+            updateCoeffs r $ idx + 1
+          else
+            updateCoeffs (r - 1) $ idx + 1
+
+type Unpacker s =
+    JpgUnpackerParameter -> MS.STVector s Int16 -> MutableMacroBlock s Int16 -> Int32
+        -> BoolReader s Int32
+
+
+prepareUnpacker :: [([(JpgUnpackerParameter, a)], L.ByteString)]
+                -> ST s ( V.Vector (V.Vector (JpgUnpackerParameter, Unpacker s))
+                        , M.STVector s BoolState)
+prepareUnpacker lst = do
+    let boolStates = V.fromList $ map snd infos
+    vec <- V.unsafeThaw boolStates
+    return (V.fromList $ map fst infos, vec)
+  where infos = map prepare lst
+        prepare ([], _) = error "progressiveUnpack, no component"
+        prepare (whole@((param, _) : _) , byteString) =
+         (V.fromList $ map (\(p,_) -> (p, unpacker)) whole, boolReader)
+           where unpacker = selection (successiveApprox param) (coefficientRange param)
+                 boolReader = initBoolStateJpg . B.concat $ L.toChunks byteString
+
+                 selection (_, 0) (0, _) = decodeFirstDC
+                 selection (_, 0) _      = decodeFirstAc
+                 selection _      (0, _) = decodeRefineDc
+                 selection _      _      = decodeRefineAc
+
+data ComponentData s = ComponentData
+  { componentIndices    :: V.Vector (VS.Vector Int)
+  , componentBlocks     :: V.Vector (MutableMacroBlock s Int16)
+  , componentId         :: !Int
+  , componentBlockCount :: !Int
+  }
+
+-- | Iteration from 0 to n in monadic context, without data
+-- keeping.
+lineMap :: (Monad m) => Int -> (Int -> m ()) -> m ()
+{-# INLINE lineMap #-}
+lineMap count f = go 0
+  where go n | n >= count = return ()
+        go n = f n >> go (n + 1)
+
+progressiveUnpack :: (Int, Int)
+                  -> JpgFrameHeader
+                  -> V.Vector (MacroBlock Int16)
+                  -> [([(JpgUnpackerParameter, a)], L.ByteString)]
+                  -> ST s (MutableImage s PixelYCbCr8)
+progressiveUnpack (maxiW, maxiH) frame quants lst = do
+    (unpackers, readers) <- prepareUnpacker lst
+    allBlocks <- mapM allocateWorkingBlocks . zip [0..] $ jpgComponents frame
+                    :: ST s [ComponentData s]
+    let scanCount = length lst
+        restartIntervalValue = case lst of
+                ((p,_):_,_): _ -> restartInterval p
+                _ -> -1
+    dcCoeffs <- MS.replicate imgComponentCount 0
+    eobRuns <- MS.replicate (length lst) 0
+    workBlock <- createEmptyMutableMacroBlock
+    writeIndices <- MS.replicate imgComponentCount (0 :: Int)
+    restartIntervals <- MS.replicate scanCount restartIntervalValue
+    let elementCount = imgWidth * imgHeight * fromIntegral imgComponentCount
+    img <- MutableImage imgWidth imgHeight <$> MS.replicate elementCount 128
+
+    let processRestartInterval =
+          forM_ [0 .. scanCount - 1] $ \ix -> do
+            v <- restartIntervals `MS.read` ix
+            if v == 0 then do
+              -- reset DC prediction
+              when (ix == 0) (MS.set dcCoeffs 0)
+              reader <- readers `M.read` ix
+              (_, updated) <- runBoolReaderWith reader $
+                  byteAlignJpg >> decodeRestartInterval
+              (readers `M.write` ix) updated 
+              (eobRuns `MS.unsafeWrite` ix) 0
+              (restartIntervals `MS.unsafeWrite` ix) $ restartIntervalValue - 1
+            else
+              (restartIntervals `MS.unsafeWrite` ix) $ v - 1
+
+
+    lineMap imageMcuHeight $ \mmY -> do
+      -- Reset all blocks to 0
+      forM_ allBlocks $ V.mapM_ (`MS.set` 0) .  componentBlocks
+      MS.set writeIndices 0
+
+      lineMap imageMcuWidth $ \_mmx -> do
+        processRestartInterval 
+        V.forM_ unpackers $ V.mapM_ $ \(unpackParam, unpacker) -> do
+            boolState <- readers `M.read` readerIndex unpackParam
+            eobrun <- eobRuns `MS.read` readerIndex unpackParam
+            let componentNumber = componentIndex unpackParam
+            writeIndex <- writeIndices `MS.read` componentNumber
+            let componentData = allBlocks !! componentNumber
+                -- We get back the correct block indices for the number of component
+                -- in the current scope (precalculated)
+                indexVector =
+                    componentIndices componentData ! indiceVector unpackParam
+                maxIndexLength = VS.length indexVector
+            unless (writeIndex + blockIndex unpackParam >= maxIndexLength) $ do
+               let realIndex = indexVector VS.! (writeIndex + blockIndex unpackParam)
+                   writeBlock = componentBlocks componentData ! realIndex
+               (eobrun', state) <-
+                   runBoolReaderWith boolState $
+                       unpacker unpackParam dcCoeffs writeBlock eobrun
+
+               (readers `M.write` readerIndex unpackParam) state
+               (eobRuns `MS.write` readerIndex unpackParam) eobrun'
+
+        -- Update the write indices
+        forM_ allBlocks $ \comp -> do
+          writeIndex <- writeIndices `MS.read` componentId comp
+          let newIndex = writeIndex + componentBlockCount comp
+          (writeIndices `MS.write` componentId comp) newIndex
+
+      forM_ allBlocks $ \compData -> do
+        let compBlocks = componentBlocks compData
+            cId = componentId compData
+            comp = jpgComponents frame !! cId
+            quantId =
+                fromIntegral $ quantizationTableDest comp
+            table = quants ! min 3 quantId
+            compW = fromIntegral $ horizontalSamplingFactor comp
+            compH = fromIntegral $ verticalSamplingFactor comp
+            cw8 = maxiW - fromIntegral (horizontalSamplingFactor comp) + 1
+            ch8 = maxiH - fromIntegral (verticalSamplingFactor comp) + 1
+
+        rasterMap (imageMcuWidth * compW) compH $ \rx y -> do
+            let ry = mmY * maxiH + y
+                block = compBlocks ! (y * imageMcuWidth * compW + rx)
+            transformed <- decodeMacroBlock table workBlock block
+            unpackMacroBlock imgComponentCount
+                    cw8 ch8 cId (rx * cw8) ry
+                    img transformed
+
+    return img
+
+  where imgComponentCount = length $ jpgComponents frame
+
+        imgWidth = fromIntegral $ jpgWidth frame
+        imgHeight = fromIntegral $ jpgHeight frame
+
+        imageBlockWidth = toBlockSize imgWidth
+        imageBlockHeight = toBlockSize imgHeight
+
+        imageMcuWidth =  (imageBlockWidth + (maxiW - 1)) `div` maxiW
+        imageMcuHeight = (imageBlockHeight + (maxiH - 1)) `div` maxiH
+
+        allocateWorkingBlocks (ix, comp) = do
+            let blockCount = hSample * vSample * imageMcuWidth * 2
+            blocks <- V.replicateM blockCount createEmptyMutableMacroBlock
+            return ComponentData 
+                { componentBlocks = blocks
+                , componentIndices = createMcuLineIndices comp imgWidth imageMcuWidth
+                , componentBlockCount = hSample * vSample
+                , componentId = ix
+                }
+            where hSample = fromIntegral $ horizontalSamplingFactor comp
+                  vSample = fromIntegral $ verticalSamplingFactor comp
+
+ src/Codec/Picture/Jpg/Internal/Types.hs view
@@ -0,0 +1,753 @@+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE CPP #-}
+module Codec.Picture.Jpg.Internal.Types( MutableMacroBlock
+                              , createEmptyMutableMacroBlock
+                              , printMacroBlock
+                              , printPureMacroBlock
+                              , DcCoefficient
+                              , JpgImage( .. )
+                              , JpgComponent( .. )
+                              , JpgFrameHeader( .. )
+                              , JpgFrame( .. )
+                              , JpgFrameKind( .. )
+                              , JpgScanHeader( .. )
+                              , JpgQuantTableSpec( .. )
+                              , JpgHuffmanTableSpec( .. )
+                              , JpgImageKind( .. )
+                              , JpgScanSpecification( .. )
+                              , JpgColorSpace( .. )
+                              , AdobeTransform( .. )
+                              , JpgAdobeApp14( .. )
+                              , JpgJFIFApp0( .. )
+                              , JFifUnit( .. )
+                              , calculateSize
+                              , dctBlockSize
+                              ) where
+
+
+#if !MIN_VERSION_base(4,8,0)
+import Control.Applicative( pure, (<*>), (<$>) )
+#endif
+
+import Control.Monad( when, replicateM, forM, forM_, unless )
+import Control.Monad.ST( ST )
+import Data.Bits( (.|.), (.&.), unsafeShiftL, unsafeShiftR )
+import Data.List( partition )
+import Data.Monoid( (<>) )
+import Foreign.Storable ( Storable )
+import Data.Vector.Unboxed( (!) )
+import qualified Data.Vector as V
+import qualified Data.Vector.Unboxed as VU
+import qualified Data.Vector.Storable as VS
+import qualified Data.Vector.Storable.Mutable as M
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Char8 as BC
+import qualified Data.ByteString.Lazy as L
+
+import Data.Int( Int16 )
+import Data.Word(Word8, Word16 )
+import Data.Binary( Binary(..) )
+
+import Data.Binary.Get( Get
+                      , getWord8
+                      , getWord16be
+                      , getByteString
+                      , skip
+                      , bytesRead
+                      )
+
+import Data.Binary.Put( Put
+                      , putWord8
+                      , putWord16be
+                      , putLazyByteString
+                      , putByteString
+                      , runPut
+                      )
+
+import Codec.Picture.InternalHelper
+import Codec.Picture.Jpg.Internal.DefaultTable
+import Codec.Picture.Tiff.Internal.Types
+import Codec.Picture.Tiff.Internal.Metadata( exifOffsetIfd )
+import Codec.Picture.Metadata.Exif
+
+{-import Debug.Trace-}
+import Text.Printf
+
+-- | Type only used to make clear what kind of integer we are carrying
+-- Might be transformed into newtype in the future
+type DcCoefficient = Int16
+
+-- | Macroblock that can be transformed.
+type MutableMacroBlock s a = M.STVector s a
+
+data JpgFrameKind =
+      JpgBaselineDCTHuffman
+    | JpgExtendedSequentialDCTHuffman
+    | JpgProgressiveDCTHuffman
+    | JpgLosslessHuffman
+    | JpgDifferentialSequentialDCTHuffman
+    | JpgDifferentialProgressiveDCTHuffman
+    | JpgDifferentialLosslessHuffman
+    | JpgExtendedSequentialArithmetic
+    | JpgProgressiveDCTArithmetic
+    | JpgLosslessArithmetic
+    | JpgDifferentialSequentialDCTArithmetic
+    | JpgDifferentialProgressiveDCTArithmetic
+    | JpgDifferentialLosslessArithmetic
+    | JpgQuantizationTable
+    | JpgHuffmanTableMarker
+    | JpgStartOfScan
+    | JpgEndOfImage
+    | JpgAppSegment Word8
+    | JpgExtensionSegment Word8
+
+    | JpgRestartInterval
+    | JpgRestartIntervalEnd Word8
+    deriving (Eq, Show)
+
+data JpgFrame =
+      JpgAppFrame        !Word8 B.ByteString
+    | JpgAdobeAPP14      !JpgAdobeApp14
+    | JpgJFIF            !JpgJFIFApp0
+    | JpgExif            ![ImageFileDirectory]
+    | JpgExtension       !Word8 B.ByteString
+    | JpgQuantTable      ![JpgQuantTableSpec]
+    | JpgHuffmanTable    ![(JpgHuffmanTableSpec, HuffmanPackedTree)]
+    | JpgScanBlob        !JpgScanHeader !L.ByteString
+    | JpgScans           !JpgFrameKind !JpgFrameHeader
+    | JpgIntervalRestart !Word16
+    deriving Show
+
+data JpgColorSpace
+  = JpgColorSpaceYCbCr
+  | JpgColorSpaceYCC
+  | JpgColorSpaceY
+  | JpgColorSpaceYA
+  | JpgColorSpaceYCCA
+  | JpgColorSpaceYCCK
+  | JpgColorSpaceCMYK
+  | JpgColorSpaceRGB
+  | JpgColorSpaceRGBA
+  deriving Show
+
+data AdobeTransform
+  = AdobeUnknown    -- ^ Value 0
+  | AdobeYCbCr      -- ^ value 1
+  | AdobeYCck       -- ^ value 2
+  deriving Show
+
+data JpgAdobeApp14 = JpgAdobeApp14
+  { _adobeDctVersion :: !Word16
+  , _adobeFlag0      :: !Word16
+  , _adobeFlag1      :: !Word16
+  , _adobeTransform  :: !AdobeTransform
+  }
+  deriving Show
+
+-- | Size: 1
+data JFifUnit
+  = JFifUnitUnknown   -- ^ 0
+  | JFifPixelsPerInch -- ^ 1
+  | JFifPixelsPerCentimeter -- ^ 2
+  deriving Show
+
+instance Binary JFifUnit where
+  put v = putWord8 $ case v of
+    JFifUnitUnknown -> 0
+    JFifPixelsPerInch -> 1
+    JFifPixelsPerCentimeter -> 2
+  get = do
+    v <- getWord8
+    pure $ case v of
+      0 -> JFifUnitUnknown
+      1 -> JFifPixelsPerInch
+      2 -> JFifPixelsPerCentimeter
+      _ -> JFifUnitUnknown
+
+data JpgJFIFApp0 = JpgJFIFApp0
+  { _jfifUnit      :: !JFifUnit
+  , _jfifDpiX      :: !Word16
+  , _jfifDpiY      :: !Word16
+  , _jfifThumbnail :: !(Maybe {- (Image PixelRGB8) -} Int)
+  }
+  deriving Show
+
+instance Binary JpgJFIFApp0 where
+  get = do
+    sig <- getByteString 5
+    when (sig /= BC.pack "JFIF\0") $
+        fail "Invalid JFIF signature"
+    major <- getWord8
+    minor <- getWord8
+    when (major /= 1 && minor > 2) $
+        fail "Unrecognize JFIF version"
+    unit <- get
+    dpiX <- getWord16be
+    dpiY <- getWord16be
+    w <- getWord8
+    h <- getWord8
+    let pxCount = 3 * w * h
+    img <- case pxCount of
+      0 -> return Nothing
+      _ -> return Nothing
+    return $ JpgJFIFApp0
+        { _jfifUnit      = unit
+        , _jfifDpiX      = dpiX
+        , _jfifDpiY      = dpiY
+        , _jfifThumbnail = img
+        }
+
+
+  put jfif = do
+    putByteString $ BC.pack "JFIF\0" -- 5
+    putWord8 1                       -- 1 6
+    putWord8 2                       -- 1 7
+    put $ _jfifUnit jfif             -- 1 8
+    putWord16be $ _jfifDpiX jfif     -- 2 10
+    putWord16be $ _jfifDpiY jfif     -- 2 12
+    putWord8 0                       -- 1 13
+    putWord8 0                       -- 1 14
+
+{-Thumbnail width (tw) 	1 	Horizontal size of embedded JFIF thumbnail in pixels-}
+{-Thumbnail height (th) 	1 	Vertical size of embedded JFIF thumbnail in pixels-}
+{-Thumbnail data 	3 × tw × th 	Uncompressed 24 bit RGB raster thumbnail-}
+
+instance Binary AdobeTransform where
+  put v = case v of
+    AdobeUnknown -> putWord8 0
+    AdobeYCbCr -> putWord8 1
+    AdobeYCck -> putWord8 2
+
+  get = do
+    v <- getWord8
+    pure $ case v of
+      0 -> AdobeUnknown
+      1 -> AdobeYCbCr
+      2 -> AdobeYCck
+      _ -> AdobeUnknown
+
+instance Binary JpgAdobeApp14 where
+  get = do
+    let sig = BC.pack "Adobe"
+    fileSig <- getByteString 5
+    when (fileSig /= sig) $
+       fail "Invalid Adobe APP14 marker"
+    version <- getWord16be
+    when (version /= 100) $
+       fail $ "Invalid Adobe APP14 version " ++ show version
+    JpgAdobeApp14 version
+                  <$> getWord16be
+                  <*> getWord16be <*> get
+
+  put (JpgAdobeApp14 v f0 f1 t) = do
+    putByteString $ BC.pack "Adobe"
+    putWord16be v
+    putWord16be f0
+    putWord16be f1
+    put t
+
+
+data JpgFrameHeader = JpgFrameHeader
+    { jpgFrameHeaderLength   :: !Word16
+    , jpgSamplePrecision     :: !Word8
+    , jpgHeight              :: !Word16
+    , jpgWidth               :: !Word16
+    , jpgImageComponentCount :: !Word8
+    , jpgComponents          :: ![JpgComponent]
+    }
+    deriving Show
+
+
+instance SizeCalculable JpgFrameHeader where
+    calculateSize hdr = 2 + 1 + 2 + 2 + 1
+                      + sum [calculateSize c | c <- jpgComponents hdr]
+
+data JpgComponent = JpgComponent
+    { componentIdentifier       :: !Word8
+      -- | Stored with 4 bits
+    , horizontalSamplingFactor  :: !Word8
+      -- | Stored with 4 bits
+    , verticalSamplingFactor    :: !Word8
+    , quantizationTableDest     :: !Word8
+    }
+    deriving Show
+
+instance SizeCalculable JpgComponent where
+    calculateSize _ = 3
+
+data JpgImage = JpgImage { jpgFrame :: [JpgFrame] }
+    deriving Show
+
+data JpgScanSpecification = JpgScanSpecification
+    { componentSelector :: !Word8
+      -- | Encoded as 4 bits
+    , dcEntropyCodingTable :: !Word8
+      -- | Encoded as 4 bits
+    , acEntropyCodingTable :: !Word8
+
+    }
+    deriving Show
+
+instance SizeCalculable JpgScanSpecification where
+    calculateSize _ = 2
+
+data JpgScanHeader = JpgScanHeader
+    { scanLength :: !Word16
+    , scanComponentCount :: !Word8
+    , scans :: [JpgScanSpecification]
+
+      -- | (begin, end)
+    , spectralSelection    :: (Word8, Word8)
+
+      -- | Encoded as 4 bits
+    , successiveApproxHigh :: !Word8
+
+      -- | Encoded as 4 bits
+    , successiveApproxLow :: !Word8
+    }
+    deriving Show
+
+instance SizeCalculable JpgScanHeader where
+    calculateSize hdr = 2 + 1
+                      + sum [calculateSize c | c <- scans hdr]
+                      + 2
+                      + 1
+
+data JpgQuantTableSpec = JpgQuantTableSpec
+    { -- | Stored on 4 bits
+      quantPrecision     :: !Word8
+
+      -- | Stored on 4 bits
+    , quantDestination   :: !Word8
+
+    , quantTable         :: MacroBlock Int16
+    }
+    deriving Show
+
+class SizeCalculable a where
+    calculateSize :: a -> Int
+
+-- | Type introduced only to avoid some typeclass overlapping
+-- problem
+newtype TableList a = TableList [a]
+
+instance (SizeCalculable a, Binary a) => Binary (TableList a) where
+    put (TableList lst) = do
+        putWord16be . fromIntegral $ sum [calculateSize table | table <- lst] + 2
+        mapM_ put lst
+
+    get = TableList <$> (getWord16be >>= \s -> innerParse (fromIntegral s - 2))
+      where innerParse :: Int -> Get [a]
+            innerParse 0    = return []
+            innerParse size = do
+                onStart <- fromIntegral <$> bytesRead
+                table <- get
+                onEnd <- fromIntegral <$> bytesRead
+                (table :) <$> innerParse (size - (onEnd - onStart))
+
+instance SizeCalculable JpgQuantTableSpec where
+    calculateSize table =
+        1 + (fromIntegral (quantPrecision table) + 1) * 64
+
+instance Binary JpgQuantTableSpec where
+    put table = do
+        let precision = quantPrecision table
+        put4BitsOfEach precision (quantDestination table)
+        forM_ (VS.toList $ quantTable table) $ \coeff ->
+            if precision == 0 then putWord8 $ fromIntegral coeff
+                             else putWord16be $ fromIntegral coeff
+
+    get = do
+        (precision, dest) <- get4BitOfEach
+        coeffs <- replicateM 64 $ if precision == 0
+                then fromIntegral <$> getWord8
+                else fromIntegral <$> getWord16be
+        return JpgQuantTableSpec
+            { quantPrecision = precision
+            , quantDestination = dest
+            , quantTable = VS.fromListN 64 coeffs
+            }
+
+data JpgHuffmanTableSpec = JpgHuffmanTableSpec
+    { -- | 0 : DC, 1 : AC, stored on 4 bits
+      huffmanTableClass       :: !DctComponent
+      -- | Stored on 4 bits
+    , huffmanTableDest        :: !Word8
+
+    , huffSizes :: !(VU.Vector Word8)
+    , huffCodes :: !(V.Vector (VU.Vector Word8))
+    }
+    deriving Show
+
+instance SizeCalculable JpgHuffmanTableSpec where
+    calculateSize table = 1 + 16 + sum [fromIntegral e | e <- VU.toList $ huffSizes table]
+
+instance Binary JpgHuffmanTableSpec where
+    put table = do
+        let classVal = if huffmanTableClass table == DcComponent
+                          then 0 else 1
+        put4BitsOfEach classVal $ huffmanTableDest table
+        mapM_ put . VU.toList $ huffSizes table
+        forM_ [0 .. 15] $ \i ->
+            when (huffSizes table ! i /= 0)
+                 (let elements = VU.toList $ huffCodes table V.! i
+                  in mapM_ put elements)
+
+    get = do
+        (huffClass, huffDest) <- get4BitOfEach
+        sizes <- replicateM 16 getWord8
+        codes <- forM sizes $ \s ->
+            VU.replicateM (fromIntegral s) getWord8
+        return JpgHuffmanTableSpec
+            { huffmanTableClass =
+                if huffClass == 0 then DcComponent else AcComponent
+            , huffmanTableDest = huffDest
+            , huffSizes = VU.fromListN 16 sizes
+            , huffCodes = V.fromListN 16 codes
+            }
+
+instance Binary JpgImage where
+    put (JpgImage { jpgFrame = frames }) =
+        putWord8 0xFF >> putWord8 0xD8 >> mapM_ putFrame frames
+            >> putWord8 0xFF >> putWord8 0xD9
+
+    get = do
+        let startOfImageMarker = 0xD8
+            -- endOfImageMarker = 0xD9
+        checkMarker commonMarkerFirstByte startOfImageMarker
+        eatUntilCode
+        frames <- parseFrames
+        {-checkMarker commonMarkerFirstByte endOfImageMarker-}
+        return JpgImage { jpgFrame = frames }
+
+eatUntilCode :: Get ()
+eatUntilCode = do
+    code <- getWord8
+    unless (code == 0xFF) eatUntilCode
+
+takeCurrentFrame :: Get B.ByteString
+takeCurrentFrame = do
+    size <- getWord16be
+    getByteString (fromIntegral size - 2)
+
+putFrame :: JpgFrame -> Put
+putFrame (JpgAdobeAPP14 adobe) = 
+    put (JpgAppSegment 14) >> putWord16be 14 >> put adobe
+putFrame (JpgJFIF jfif) =
+    put (JpgAppSegment 0) >> putWord16be (14+2) >> put jfif
+putFrame (JpgExif exif) = putExif exif
+putFrame (JpgAppFrame appCode str) =
+    put (JpgAppSegment appCode) >> putWord16be (fromIntegral $ B.length str) >> put str
+putFrame (JpgExtension appCode str) =
+    put (JpgExtensionSegment appCode) >> putWord16be (fromIntegral $ B.length str) >> put str
+putFrame (JpgQuantTable tables) =
+    put JpgQuantizationTable >> put (TableList tables)
+putFrame (JpgHuffmanTable tables) =
+    put JpgHuffmanTableMarker >> put (TableList $ map fst tables)
+putFrame (JpgIntervalRestart size) =
+    put JpgRestartInterval >> put (RestartInterval size)
+putFrame (JpgScanBlob hdr blob) =
+    put JpgStartOfScan >> put hdr >> putLazyByteString blob
+putFrame (JpgScans kind hdr) =
+    put kind >> put hdr
+
+--------------------------------------------------
+----            Serialization instances
+--------------------------------------------------
+commonMarkerFirstByte :: Word8
+commonMarkerFirstByte = 0xFF
+
+checkMarker :: Word8 -> Word8 -> Get ()
+checkMarker b1 b2 = do
+    rb1 <- getWord8
+    rb2 <- getWord8
+    when (rb1 /= b1 || rb2 /= b2)
+         (fail "Invalid marker used")
+
+extractScanContent :: L.ByteString -> (L.ByteString, L.ByteString)
+extractScanContent str = aux 0
+  where maxi = fromIntegral $ L.length str - 1
+
+        aux n | n >= maxi = (str, L.empty)
+              | v == 0xFF && vNext /= 0 && not isReset = L.splitAt n str
+              | otherwise = aux (n + 1)
+            where v = str `L.index` n
+                  vNext = str `L.index` (n + 1)
+                  isReset = 0xD0 <= vNext && vNext <= 0xD7
+
+parseAdobe14 :: B.ByteString -> [JpgFrame] -> [JpgFrame]
+parseAdobe14 str lst = go where
+  go = case runGetStrict get str of
+    Left _err -> lst
+    Right app14 -> JpgAdobeAPP14 app14 : lst
+
+-- | Parse JFIF or JFXX information. Right now only JFIF.
+parseJF__  :: B.ByteString -> [JpgFrame] -> [JpgFrame]
+parseJF__  str lst = go where
+  go = case runGetStrict get str of
+    Left _err -> lst
+    Right jfif -> JpgJFIF jfif : lst
+
+parseExif :: B.ByteString -> [JpgFrame] -> [JpgFrame]
+parseExif str lst 
+  | exifHeader `B.isPrefixOf` str = go
+  | otherwise = lst
+  where
+    exifHeader = BC.pack "Exif\0\0"
+    tiff = B.drop (B.length exifHeader) str
+    go = case runGetStrict (getP tiff) tiff of
+      Left _err -> lst
+      Right (_hdr :: TiffHeader, []) -> lst
+      Right (_hdr :: TiffHeader, ifds : _) -> JpgExif ifds : lst
+
+putExif :: [ImageFileDirectory] -> Put
+putExif ifds = putAll where
+  hdr = TiffHeader
+    { hdrEndianness = EndianBig
+    , hdrOffset = 8
+    }
+
+  ifdList = case partition (isInIFD0 . ifdIdentifier) ifds of
+    (ifd0, []) -> [ifd0]
+    (ifd0, ifdExif) -> [ifd0 <> pure exifOffsetIfd, ifdExif]
+  
+  exifBlob = runPut $ do
+    putByteString $ BC.pack "Exif\0\0"
+    putP BC.empty (hdr, ifdList)
+
+  putAll = do
+    put (JpgAppSegment 1)
+    putWord16be . fromIntegral $ L.length exifBlob + 2
+    putLazyByteString exifBlob
+
+parseFrames :: Get [JpgFrame]
+parseFrames = do
+    kind <- get
+    let parseNextFrame = do
+            word <- getWord8
+            when (word /= 0xFF) $ do
+                readedData <- bytesRead
+                fail $ "Invalid Frame marker (" ++ show word
+                     ++ ", bytes read : " ++ show readedData ++ ")"
+            parseFrames
+
+    case kind of
+        JpgEndOfImage -> return []
+        JpgAppSegment 0 ->
+            parseJF__ <$> takeCurrentFrame <*> parseNextFrame
+        JpgAppSegment 1 ->
+            parseExif <$> takeCurrentFrame <*> parseNextFrame
+        JpgAppSegment 14 ->
+            parseAdobe14 <$> takeCurrentFrame <*> parseNextFrame
+        JpgAppSegment c ->
+            (\frm lst -> JpgAppFrame c frm : lst) <$> takeCurrentFrame <*> parseNextFrame
+        JpgExtensionSegment c ->
+            (\frm lst -> JpgExtension c frm : lst) <$> takeCurrentFrame <*> parseNextFrame
+        JpgQuantizationTable ->
+            (\(TableList quants) lst -> JpgQuantTable quants : lst) <$> get <*> parseNextFrame
+        JpgRestartInterval ->
+            (\(RestartInterval i) lst -> JpgIntervalRestart i : lst) <$> get <*> parseNextFrame
+        JpgHuffmanTableMarker ->
+            (\(TableList huffTables) lst ->
+                    JpgHuffmanTable [(t, packHuffmanTree . buildPackedHuffmanTree $ huffCodes t) | t <- huffTables] : lst)
+                    <$> get <*> parseNextFrame
+        JpgStartOfScan ->
+            (\frm imgData ->
+                let (d, other) = extractScanContent imgData
+                in
+                case runGet parseFrames (L.drop 1 other) of
+                  Left _ -> [JpgScanBlob frm d]
+                  Right lst -> JpgScanBlob frm d : lst
+            ) <$> get <*> getRemainingLazyBytes
+
+        _ -> (\hdr lst -> JpgScans kind hdr : lst) <$> get <*> parseNextFrame
+
+buildPackedHuffmanTree :: V.Vector (VU.Vector Word8) -> HuffmanTree
+buildPackedHuffmanTree = buildHuffmanTree . map VU.toList . V.toList
+
+secondStartOfFrameByteOfKind :: JpgFrameKind -> Word8
+secondStartOfFrameByteOfKind = aux
+  where
+    aux JpgBaselineDCTHuffman = 0xC0
+    aux JpgExtendedSequentialDCTHuffman = 0xC1
+    aux JpgProgressiveDCTHuffman = 0xC2
+    aux JpgLosslessHuffman = 0xC3
+    aux JpgDifferentialSequentialDCTHuffman = 0xC5
+    aux JpgDifferentialProgressiveDCTHuffman = 0xC6
+    aux JpgDifferentialLosslessHuffman = 0xC7
+    aux JpgExtendedSequentialArithmetic = 0xC9
+    aux JpgProgressiveDCTArithmetic = 0xCA
+    aux JpgLosslessArithmetic = 0xCB
+    aux JpgHuffmanTableMarker = 0xC4
+    aux JpgDifferentialSequentialDCTArithmetic = 0xCD
+    aux JpgDifferentialProgressiveDCTArithmetic = 0xCE
+    aux JpgDifferentialLosslessArithmetic = 0xCF
+    aux JpgEndOfImage = 0xD9
+    aux JpgQuantizationTable = 0xDB
+    aux JpgStartOfScan = 0xDA
+    aux JpgRestartInterval = 0xDD
+    aux (JpgRestartIntervalEnd v) = v
+    aux (JpgAppSegment a) = (a + 0xE0)
+    aux (JpgExtensionSegment a) = a
+
+data JpgImageKind = BaseLineDCT | ProgressiveDCT
+
+instance Binary JpgFrameKind where
+    put v = putWord8 0xFF >> put (secondStartOfFrameByteOfKind v)
+    get = do
+        -- no lookahead :(
+        {-word <- getWord8-}
+        word2 <- getWord8
+        return $ case word2 of
+            0xC0 -> JpgBaselineDCTHuffman
+            0xC1 -> JpgExtendedSequentialDCTHuffman
+            0xC2 -> JpgProgressiveDCTHuffman
+            0xC3 -> JpgLosslessHuffman
+            0xC4 -> JpgHuffmanTableMarker
+            0xC5 -> JpgDifferentialSequentialDCTHuffman
+            0xC6 -> JpgDifferentialProgressiveDCTHuffman
+            0xC7 -> JpgDifferentialLosslessHuffman
+            0xC9 -> JpgExtendedSequentialArithmetic
+            0xCA -> JpgProgressiveDCTArithmetic
+            0xCB -> JpgLosslessArithmetic
+            0xCD -> JpgDifferentialSequentialDCTArithmetic
+            0xCE -> JpgDifferentialProgressiveDCTArithmetic
+            0xCF -> JpgDifferentialLosslessArithmetic
+            0xD9 -> JpgEndOfImage
+            0xDA -> JpgStartOfScan
+            0xDB -> JpgQuantizationTable
+            0xDD -> JpgRestartInterval
+            a | a >= 0xF0 -> JpgExtensionSegment a
+              | a >= 0xE0 -> JpgAppSegment (a - 0xE0)
+              | a >= 0xD0 && a <= 0xD7 -> JpgRestartIntervalEnd a
+              | otherwise -> error ("Invalid frame marker (" ++ show a ++ ")")
+
+put4BitsOfEach :: Word8 -> Word8 -> Put
+put4BitsOfEach a b = put $ (a `unsafeShiftL` 4) .|. b
+
+get4BitOfEach :: Get (Word8, Word8)
+get4BitOfEach = do
+    val <- get
+    return ((val `unsafeShiftR` 4) .&. 0xF, val .&. 0xF)
+
+newtype RestartInterval = RestartInterval Word16
+
+instance Binary RestartInterval where
+    put (RestartInterval i) = putWord16be 4 >> putWord16be i
+    get = do
+        size <- getWord16be
+        when (size /= 4) (fail "Invalid jpeg restart interval size")
+        RestartInterval <$> getWord16be
+
+instance Binary JpgComponent where
+    get = do
+        ident <- getWord8
+        (horiz, vert) <- get4BitOfEach
+        quantTableIndex <- getWord8
+        return JpgComponent
+            { componentIdentifier = ident
+            , horizontalSamplingFactor = horiz
+            , verticalSamplingFactor = vert
+            , quantizationTableDest = quantTableIndex
+            }
+    put v = do
+        put $ componentIdentifier v
+        put4BitsOfEach (horizontalSamplingFactor v) $ verticalSamplingFactor v
+        put $ quantizationTableDest v
+
+instance Binary JpgFrameHeader where
+    get = do
+        beginOffset <- fromIntegral <$> bytesRead
+        frmHLength <- getWord16be
+        samplePrec <- getWord8
+        h <- getWord16be
+        w <- getWord16be
+        compCount <- getWord8
+        components <- replicateM (fromIntegral compCount) get
+        endOffset <- fromIntegral <$> bytesRead
+        when (beginOffset - endOffset < fromIntegral frmHLength)
+             (skip $ fromIntegral frmHLength - (endOffset - beginOffset))
+        return JpgFrameHeader
+            { jpgFrameHeaderLength = frmHLength
+            , jpgSamplePrecision = samplePrec
+            , jpgHeight = h
+            , jpgWidth = w
+            , jpgImageComponentCount = compCount
+            , jpgComponents = components
+            }
+
+    put v = do
+        putWord16be $ jpgFrameHeaderLength v
+        putWord8    $ jpgSamplePrecision v
+        putWord16be $ jpgHeight v
+        putWord16be $ jpgWidth v
+        putWord8    $ jpgImageComponentCount v
+        mapM_ put   $ jpgComponents v
+
+instance Binary JpgScanSpecification where
+    put v = do
+        put $ componentSelector v
+        put4BitsOfEach (dcEntropyCodingTable v) $ acEntropyCodingTable v
+
+    get = do
+        compSel <- get
+        (dc, ac) <- get4BitOfEach
+        return JpgScanSpecification {
+            componentSelector = compSel
+          , dcEntropyCodingTable = dc
+          , acEntropyCodingTable = ac
+          }
+
+instance Binary JpgScanHeader where
+    get = do
+        thisScanLength <- getWord16be
+        compCount <- getWord8
+        comp <- replicateM (fromIntegral compCount) get
+        specBeg <- get
+        specEnd <- get
+        (approxHigh, approxLow) <- get4BitOfEach
+
+        return JpgScanHeader {
+            scanLength = thisScanLength,
+            scanComponentCount = compCount,
+            scans = comp,
+            spectralSelection = (specBeg, specEnd),
+            successiveApproxHigh = approxHigh,
+            successiveApproxLow = approxLow
+        }
+
+    put v = do
+        putWord16be $ scanLength v
+        putWord8 $ scanComponentCount v
+        mapM_ put $ scans v
+        putWord8 . fst $ spectralSelection v
+        putWord8 . snd $ spectralSelection v
+        put4BitsOfEach (successiveApproxHigh v) $ successiveApproxLow v
+
+{-# INLINE createEmptyMutableMacroBlock #-}
+-- | Create a new macroblock with the good array size
+createEmptyMutableMacroBlock :: (Storable a, Num a) => ST s (MutableMacroBlock s a)
+createEmptyMutableMacroBlock = M.replicate 64 0
+
+printMacroBlock :: (Storable a, PrintfArg a)
+                => MutableMacroBlock s a -> ST s String
+printMacroBlock block = pLn 0
+    where pLn 64 = return "===============================\n"
+          pLn i = do
+              v <- block `M.unsafeRead` i
+              vn <- pLn (i+1)
+              return $ printf (if i `mod` 8 == 0 then "\n%5d " else "%5d ") v ++ vn
+
+printPureMacroBlock :: (Storable a, PrintfArg a) => MacroBlock a -> String
+printPureMacroBlock block = pLn 0
+    where pLn 64 = "===============================\n"
+          pLn i = str ++ pLn (i + 1)
+            where str | i `mod` 8 == 0 = printf "\n%5d " v
+                      | otherwise = printf "%5d" v
+                  v = block VS.! i
+
+
+{-# INLINE dctBlockSize #-}
+dctBlockSize :: Num a => a
+dctBlockSize = 8
− src/Codec/Picture/Jpg/Metadata.hs
@@ -1,41 +0,0 @@-{-# LANGUAGE CPP #-}-module Codec.Picture.Jpg.Metadata ( extractMetadatas, encodeMetadatas ) where--#if !MIN_VERSION_base(4,8,0)-import Control.Applicative( pure )-import Data.Monoid( mempty )-import Data.Word( Word )-#endif--import Data.Word( Word16 )-import Data.Maybe( fromMaybe )-import qualified Codec.Picture.Metadata as Met-import Codec.Picture.Metadata( Metadatas )-import Codec.Picture.Jpg.Types--scalerOfUnit :: JFifUnit -> Met.Keys Word -> Word16 -> Metadatas -> Metadatas-scalerOfUnit unit k v = case unit of-  JFifUnitUnknown -> id-  JFifPixelsPerInch -> Met.insert k (fromIntegral v)-  JFifPixelsPerCentimeter ->-    Met.insert k (Met.dotsPerCentiMeterToDotPerInch $ fromIntegral v)--extractMetadatas :: JpgJFIFApp0 -> Metadatas-extractMetadatas jfif = -    inserter Met.DpiX (_jfifDpiX jfif)-        $ inserter Met.DpiY (_jfifDpiY jfif) mempty-  where-    inserter = scalerOfUnit $ _jfifUnit jfif---encodeMetadatas :: Metadatas -> [JpgFrame]-encodeMetadatas metas = fromMaybe [] $ do-  dpiX <- Met.lookup Met.DpiX metas-  dpiY <- Met.lookup Met.DpiY metas-  pure . pure . JpgJFIF $ JpgJFIFApp0-    { _jfifUnit      = JFifPixelsPerInch-    , _jfifDpiX      = fromIntegral dpiX-    , _jfifDpiY      = fromIntegral dpiY-    , _jfifThumbnail = Nothing-    }-
− src/Codec/Picture/Jpg/Progressive.hs
@@ -1,332 +0,0 @@-{-# LANGUAGE TupleSections #-}
-{-# LANGUAGE TypeSynonymInstances #-}
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE CPP #-}
-module Codec.Picture.Jpg.Progressive
-    ( JpgUnpackerParameter( .. )
-    , progressiveUnpack
-    ) where
-
-#if !MIN_VERSION_base(4,8,0)
-import Control.Applicative( pure, (<$>) )
-#endif
-
-import Control.Monad( when, unless, forM_ )
-import Control.Monad.ST( ST )
-import Control.Monad.Trans( lift )
-import Data.Bits( (.&.), (.|.), unsafeShiftL )
-import Data.Int( Int16, Int32 )
-import qualified Data.ByteString as B
-import qualified Data.ByteString.Lazy as L
-import qualified Data.Vector as V
-import qualified Data.Vector.Storable as VS
-import Data.Vector( (!) )
-import qualified Data.Vector.Mutable as M
-import qualified Data.Vector.Storable.Mutable as MS
-
-import Codec.Picture.Types
-import Codec.Picture.BitWriter
-import Codec.Picture.Jpg.Common
-import Codec.Picture.Jpg.Types
-import Codec.Picture.Jpg.DefaultTable
-
-createMcuLineIndices :: JpgComponent -> Int -> Int -> V.Vector (VS.Vector Int)
-createMcuLineIndices param imgWidth mcuWidth =
- V.fromList $ VS.fromList <$> [indexSolo, indexMulti]
-  where compW = fromIntegral $ horizontalSamplingFactor param
-        compH = fromIntegral $ verticalSamplingFactor param
-        imageBlockSize = toBlockSize imgWidth
-
-        -- if the displayed MCU block is only displayed in half (like with
-        -- width 500 then we loose one macroblock of the MCU at the end of
-        -- the line. Previous implementation which naively used full mcu
-        -- was wrong. Only taking into account visible macroblocks
-        indexSolo = [base + x
-            | y <- [0 .. compH - 1]
-            , let base = y * mcuWidth * compW
-            , x <- [0 .. imageBlockSize - 1]]
-
-        indexMulti =
-            [(mcu + y * mcuWidth) * compW + x
-                | mcu <- [0 .. mcuWidth - 1]
-                , y <- [0 .. compH - 1]
-                , x <- [0 .. compW - 1] ]
-
-decodeFirstDC :: JpgUnpackerParameter
-              -> MS.STVector s Int16
-              -> MutableMacroBlock s Int16
-              -> Int32
-              -> BoolReader s Int32
-decodeFirstDC params dcCoeffs block eobrun = unpack >> pure eobrun
-  where unpack = do
-          (dcDeltaCoefficient) <- dcCoefficientDecode $ dcHuffmanTree params
-          previousDc <- lift $ dcCoeffs `MS.unsafeRead` componentIndex params
-          let neoDcCoefficient = previousDc + dcDeltaCoefficient
-              approxLow = fst $ successiveApprox params
-              scaledDc = neoDcCoefficient `unsafeShiftL` approxLow
-          lift $ (block `MS.unsafeWrite` 0) scaledDc 
-          lift $ (dcCoeffs `MS.unsafeWrite` componentIndex params) neoDcCoefficient
-
-decodeRefineDc :: JpgUnpackerParameter
-               -> a
-               -> MutableMacroBlock s Int16
-               -> Int32
-               -> BoolReader s Int32
-decodeRefineDc params _ block eobrun = unpack >> pure eobrun
-  where approxLow = fst $ successiveApprox params
-        plusOne = 1 `unsafeShiftL` approxLow
-        unpack = do
-            bit <- getNextBitJpg
-            when bit . lift $ do
-                v <- block `MS.unsafeRead` 0
-                (block `MS.unsafeWrite` 0) $ v .|. plusOne
-
-decodeFirstAc :: JpgUnpackerParameter
-              -> a
-              -> MutableMacroBlock s Int16
-              -> Int32
-              -> BoolReader s Int32
-decodeFirstAc _params _ _block eobrun | eobrun > 0 = pure $ eobrun - 1
-decodeFirstAc params _ block _ = unpack startIndex
-  where (startIndex, maxIndex) = coefficientRange params
-        (low, _) = successiveApprox params
-        unpack n | n > maxIndex = pure 0
-        unpack n = do
-            rrrrssss <- decodeRrrrSsss $ acHuffmanTree params
-            case rrrrssss of
-                (0xF, 0) -> unpack $ n + 16
-                (  0, 0) -> return 0
-                (  r, 0) -> eobrun <$> unpackInt r
-                    where eobrun lowBits = (1 `unsafeShiftL` r) - 1 + lowBits
-                (  r, s) -> do
-                    let n' = n + r
-                    val <- (`unsafeShiftL` low) <$> decodeInt s
-                    lift . (block `MS.unsafeWrite` n') $ fromIntegral val
-                    unpack $ n' + 1
-
-decodeRefineAc :: forall a s. JpgUnpackerParameter
-               -> a
-               -> MutableMacroBlock s Int16
-               -> Int32
-               -> BoolReader s Int32
-decodeRefineAc params _ block eobrun
-        | eobrun == 0 = unpack startIndex
-        | otherwise   = performEobRun startIndex >> return (eobrun - 1)
-  where (startIndex, maxIndex) = coefficientRange params
-        (low, _) = successiveApprox params
-        plusOne = 1 `unsafeShiftL` low
-        minusOne = (-1) `unsafeShiftL` low
-
-        getBitVal = do
-            v <- getNextBitJpg
-            pure $ if v then plusOne else minusOne
-
-        performEobRun idx | idx > maxIndex = pure ()
-        performEobRun idx = do
-          coeff <- lift $ block `MS.unsafeRead` idx
-          if coeff /= 0 then do
-            bit <- getNextBitJpg
-            case (bit, (coeff .&. plusOne) == 0) of
-               (False, _)    -> performEobRun $ idx + 1
-               (True, False) -> performEobRun $ idx + 1
-               (True, True) -> do
-                   let newVal | coeff >= 0 = coeff + plusOne
-                              | otherwise = coeff + minusOne
-                   lift $ (block `MS.unsafeWrite` idx) newVal
-                   performEobRun $ idx + 1
-          else
-            performEobRun $ idx + 1
-
-        unpack idx | idx > maxIndex = pure 0
-        unpack idx = do
-            rrrrssss <- decodeRrrrSsss $ acHuffmanTree params
-            case rrrrssss of
-              (0xF, 0) -> do
-                idx' <- updateCoeffs 0xF idx
-                unpack $ idx' + 1
-
-              (  r, 0) -> do
-                  lowBits <- unpackInt r
-                  let newEobRun = (1 `unsafeShiftL` r) + lowBits - 1
-                  performEobRun idx
-                  pure newEobRun
-
-              (  r, _) -> do
-                  val <- getBitVal
-                  idx' <- updateCoeffs (fromIntegral r) idx
-                  when (idx' <= maxIndex) $
-                       lift $ (block `MS.unsafeWrite` idx') val
-                  unpack $ idx' + 1
-
-        updateCoeffs :: Int -> Int -> BoolReader s Int
-        updateCoeffs r idx
-            | r   < 0        = pure $ idx - 1
-            | idx > maxIndex = pure idx
-        updateCoeffs r idx = do
-          coeff <- lift $ block `MS.unsafeRead` idx
-          if coeff /= 0 then do
-            bit <- getNextBitJpg
-            when (bit && coeff .&. plusOne == 0) $ do
-              let writeCoeff | coeff >= 0 = coeff + plusOne
-                             | otherwise = coeff + minusOne
-              lift $ (block `MS.unsafeWrite` idx) writeCoeff
-            updateCoeffs r $ idx + 1
-          else
-            updateCoeffs (r - 1) $ idx + 1
-
-type Unpacker s =
-    JpgUnpackerParameter -> MS.STVector s Int16 -> MutableMacroBlock s Int16 -> Int32
-        -> BoolReader s Int32
-
-
-prepareUnpacker :: [([(JpgUnpackerParameter, a)], L.ByteString)]
-                -> ST s ( V.Vector (V.Vector (JpgUnpackerParameter, Unpacker s))
-                        , M.STVector s BoolState)
-prepareUnpacker lst = do
-    let boolStates = V.fromList $ map snd infos
-    vec <- V.unsafeThaw boolStates
-    return (V.fromList $ map fst infos, vec)
-  where infos = map prepare lst
-        prepare ([], _) = error "progressiveUnpack, no component"
-        prepare (whole@((param, _) : _) , byteString) =
-         (V.fromList $ map (\(p,_) -> (p, unpacker)) whole, boolReader)
-           where unpacker = selection (successiveApprox param) (coefficientRange param)
-                 boolReader = initBoolStateJpg . B.concat $ L.toChunks byteString
-
-                 selection (_, 0) (0, _) = decodeFirstDC
-                 selection (_, 0) _      = decodeFirstAc
-                 selection _      (0, _) = decodeRefineDc
-                 selection _      _      = decodeRefineAc
-
-data ComponentData s = ComponentData
-  { componentIndices    :: V.Vector (VS.Vector Int)
-  , componentBlocks     :: V.Vector (MutableMacroBlock s Int16)
-  , componentId         :: !Int
-  , componentBlockCount :: !Int
-  }
-
--- | Iteration from 0 to n in monadic context, without data
--- keeping.
-lineMap :: (Monad m) => Int -> (Int -> m ()) -> m ()
-{-# INLINE lineMap #-}
-lineMap count f = go 0
-  where go n | n >= count = return ()
-        go n = f n >> go (n + 1)
-
-progressiveUnpack :: (Int, Int)
-                  -> JpgFrameHeader
-                  -> V.Vector (MacroBlock Int16)
-                  -> [([(JpgUnpackerParameter, a)], L.ByteString)]
-                  -> ST s (MutableImage s PixelYCbCr8)
-progressiveUnpack (maxiW, maxiH) frame quants lst = do
-    (unpackers, readers) <- prepareUnpacker lst
-    allBlocks <- mapM allocateWorkingBlocks . zip [0..] $ jpgComponents frame
-                    :: ST s [ComponentData s]
-    let scanCount = length lst
-        restartIntervalValue = case lst of
-                ((p,_):_,_): _ -> restartInterval p
-                _ -> -1
-    dcCoeffs <- MS.replicate imgComponentCount 0
-    eobRuns <- MS.replicate (length lst) 0
-    workBlock <- createEmptyMutableMacroBlock
-    writeIndices <- MS.replicate imgComponentCount (0 :: Int)
-    restartIntervals <- MS.replicate scanCount restartIntervalValue
-    let elementCount = imgWidth * imgHeight * fromIntegral imgComponentCount
-    img <- MutableImage imgWidth imgHeight <$> MS.replicate elementCount 128
-
-    let processRestartInterval =
-          forM_ [0 .. scanCount - 1] $ \ix -> do
-            v <- restartIntervals `MS.read` ix
-            if v == 0 then do
-              -- reset DC prediction
-              when (ix == 0) (MS.set dcCoeffs 0)
-              reader <- readers `M.read` ix
-              (_, updated) <- runBoolReaderWith reader $
-                  byteAlignJpg >> decodeRestartInterval
-              (readers `M.write` ix) updated 
-              (eobRuns `MS.unsafeWrite` ix) 0
-              (restartIntervals `MS.unsafeWrite` ix) $ restartIntervalValue - 1
-            else
-              (restartIntervals `MS.unsafeWrite` ix) $ v - 1
-
-
-    lineMap imageMcuHeight $ \mmY -> do
-      -- Reset all blocks to 0
-      forM_ allBlocks $ V.mapM_ (`MS.set` 0) .  componentBlocks
-      MS.set writeIndices 0
-
-      lineMap imageMcuWidth $ \_mmx -> do
-        processRestartInterval 
-        V.forM_ unpackers $ V.mapM_ $ \(unpackParam, unpacker) -> do
-            boolState <- readers `M.read` readerIndex unpackParam
-            eobrun <- eobRuns `MS.read` readerIndex unpackParam
-            let componentNumber = componentIndex unpackParam
-            writeIndex <- writeIndices `MS.read` componentNumber
-            let componentData = allBlocks !! componentNumber
-                -- We get back the correct block indices for the number of component
-                -- in the current scope (precalculated)
-                indexVector =
-                    componentIndices componentData ! indiceVector unpackParam
-                maxIndexLength = VS.length indexVector
-            unless (writeIndex + blockIndex unpackParam >= maxIndexLength) $ do
-               let realIndex = indexVector VS.! (writeIndex + blockIndex unpackParam)
-                   writeBlock = componentBlocks componentData ! realIndex
-               (eobrun', state) <-
-                   runBoolReaderWith boolState $
-                       unpacker unpackParam dcCoeffs writeBlock eobrun
-
-               (readers `M.write` readerIndex unpackParam) state
-               (eobRuns `MS.write` readerIndex unpackParam) eobrun'
-
-        -- Update the write indices
-        forM_ allBlocks $ \comp -> do
-          writeIndex <- writeIndices `MS.read` componentId comp
-          let newIndex = writeIndex + componentBlockCount comp
-          (writeIndices `MS.write` componentId comp) newIndex
-
-      forM_ allBlocks $ \compData -> do
-        let compBlocks = componentBlocks compData
-            cId = componentId compData
-            comp = jpgComponents frame !! cId
-            quantId =
-                fromIntegral $ quantizationTableDest comp
-            table = quants ! min 3 quantId
-            compW = fromIntegral $ horizontalSamplingFactor comp
-            compH = fromIntegral $ verticalSamplingFactor comp
-            cw8 = maxiW - fromIntegral (horizontalSamplingFactor comp) + 1
-            ch8 = maxiH - fromIntegral (verticalSamplingFactor comp) + 1
-
-        rasterMap (imageMcuWidth * compW) compH $ \rx y -> do
-            let ry = mmY * maxiH + y
-                block = compBlocks ! (y * imageMcuWidth * compW + rx)
-            transformed <- decodeMacroBlock table workBlock block
-            unpackMacroBlock imgComponentCount
-                    cw8 ch8 cId (rx * cw8) ry
-                    img transformed
-
-    return img
-
-  where imgComponentCount = length $ jpgComponents frame
-
-        imgWidth = fromIntegral $ jpgWidth frame
-        imgHeight = fromIntegral $ jpgHeight frame
-
-        imageBlockWidth = toBlockSize imgWidth
-        imageBlockHeight = toBlockSize imgHeight
-
-        imageMcuWidth =  (imageBlockWidth + (maxiW - 1)) `div` maxiW
-        imageMcuHeight = (imageBlockHeight + (maxiH - 1)) `div` maxiH
-
-        allocateWorkingBlocks (ix, comp) = do
-            let blockCount = hSample * vSample * imageMcuWidth * 2
-            blocks <- V.replicateM blockCount createEmptyMutableMacroBlock
-            return ComponentData 
-                { componentBlocks = blocks
-                , componentIndices = createMcuLineIndices comp imgWidth imageMcuWidth
-                , componentBlockCount = hSample * vSample
-                , componentId = ix
-                }
-            where hSample = fromIntegral $ horizontalSamplingFactor comp
-                  vSample = fromIntegral $ verticalSamplingFactor comp
-
− src/Codec/Picture/Jpg/Types.hs
@@ -1,753 +0,0 @@-{-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE CPP #-}
-module Codec.Picture.Jpg.Types( MutableMacroBlock
-                              , createEmptyMutableMacroBlock
-                              , printMacroBlock
-                              , printPureMacroBlock
-                              , DcCoefficient
-                              , JpgImage( .. )
-                              , JpgComponent( .. )
-                              , JpgFrameHeader( .. )
-                              , JpgFrame( .. )
-                              , JpgFrameKind( .. )
-                              , JpgScanHeader( .. )
-                              , JpgQuantTableSpec( .. )
-                              , JpgHuffmanTableSpec( .. )
-                              , JpgImageKind( .. )
-                              , JpgScanSpecification( .. )
-                              , JpgColorSpace( .. )
-                              , AdobeTransform( .. )
-                              , JpgAdobeApp14( .. )
-                              , JpgJFIFApp0( .. )
-                              , JFifUnit( .. )
-                              , calculateSize
-                              , dctBlockSize
-                              ) where
-
-
-#if !MIN_VERSION_base(4,8,0)
-import Control.Applicative( pure, (<*>), (<$>) )
-#endif
-
-import Control.Monad( when, replicateM, forM, forM_, unless )
-import Control.Monad.ST( ST )
-import Data.Bits( (.|.), (.&.), unsafeShiftL, unsafeShiftR )
-import Data.List( partition )
-import Data.Monoid( (<>) )
-import Foreign.Storable ( Storable )
-import Data.Vector.Unboxed( (!) )
-import qualified Data.Vector as V
-import qualified Data.Vector.Unboxed as VU
-import qualified Data.Vector.Storable as VS
-import qualified Data.Vector.Storable.Mutable as M
-import qualified Data.ByteString as B
-import qualified Data.ByteString.Char8 as BC
-import qualified Data.ByteString.Lazy as L
-
-import Data.Int( Int16 )
-import Data.Word(Word8, Word16 )
-import Data.Binary( Binary(..) )
-
-import Data.Binary.Get( Get
-                      , getWord8
-                      , getWord16be
-                      , getByteString
-                      , skip
-                      , bytesRead
-                      )
-
-import Data.Binary.Put( Put
-                      , putWord8
-                      , putWord16be
-                      , putLazyByteString
-                      , putByteString
-                      , runPut
-                      )
-
-import Codec.Picture.InternalHelper
-import Codec.Picture.Jpg.DefaultTable
-import Codec.Picture.Tiff.Types
-import Codec.Picture.Tiff.Metadata( exifOffsetIfd )
-import Codec.Picture.Metadata.Exif
-
-{-import Debug.Trace-}
-import Text.Printf
-
--- | Type only used to make clear what kind of integer we are carrying
--- Might be transformed into newtype in the future
-type DcCoefficient = Int16
-
--- | Macroblock that can be transformed.
-type MutableMacroBlock s a = M.STVector s a
-
-data JpgFrameKind =
-      JpgBaselineDCTHuffman
-    | JpgExtendedSequentialDCTHuffman
-    | JpgProgressiveDCTHuffman
-    | JpgLosslessHuffman
-    | JpgDifferentialSequentialDCTHuffman
-    | JpgDifferentialProgressiveDCTHuffman
-    | JpgDifferentialLosslessHuffman
-    | JpgExtendedSequentialArithmetic
-    | JpgProgressiveDCTArithmetic
-    | JpgLosslessArithmetic
-    | JpgDifferentialSequentialDCTArithmetic
-    | JpgDifferentialProgressiveDCTArithmetic
-    | JpgDifferentialLosslessArithmetic
-    | JpgQuantizationTable
-    | JpgHuffmanTableMarker
-    | JpgStartOfScan
-    | JpgEndOfImage
-    | JpgAppSegment Word8
-    | JpgExtensionSegment Word8
-
-    | JpgRestartInterval
-    | JpgRestartIntervalEnd Word8
-    deriving (Eq, Show)
-
-data JpgFrame =
-      JpgAppFrame        !Word8 B.ByteString
-    | JpgAdobeAPP14      !JpgAdobeApp14
-    | JpgJFIF            !JpgJFIFApp0
-    | JpgExif            ![ImageFileDirectory]
-    | JpgExtension       !Word8 B.ByteString
-    | JpgQuantTable      ![JpgQuantTableSpec]
-    | JpgHuffmanTable    ![(JpgHuffmanTableSpec, HuffmanPackedTree)]
-    | JpgScanBlob        !JpgScanHeader !L.ByteString
-    | JpgScans           !JpgFrameKind !JpgFrameHeader
-    | JpgIntervalRestart !Word16
-    deriving Show
-
-data JpgColorSpace
-  = JpgColorSpaceYCbCr
-  | JpgColorSpaceYCC
-  | JpgColorSpaceY
-  | JpgColorSpaceYA
-  | JpgColorSpaceYCCA
-  | JpgColorSpaceYCCK
-  | JpgColorSpaceCMYK
-  | JpgColorSpaceRGB
-  | JpgColorSpaceRGBA
-  deriving Show
-
-data AdobeTransform
-  = AdobeUnknown    -- ^ Value 0
-  | AdobeYCbCr      -- ^ value 1
-  | AdobeYCck       -- ^ value 2
-  deriving Show
-
-data JpgAdobeApp14 = JpgAdobeApp14
-  { _adobeDctVersion :: !Word16
-  , _adobeFlag0      :: !Word16
-  , _adobeFlag1      :: !Word16
-  , _adobeTransform  :: !AdobeTransform
-  }
-  deriving Show
-
--- | Size: 1
-data JFifUnit
-  = JFifUnitUnknown   -- ^ 0
-  | JFifPixelsPerInch -- ^ 1
-  | JFifPixelsPerCentimeter -- ^ 2
-  deriving Show
-
-instance Binary JFifUnit where
-  put v = putWord8 $ case v of
-    JFifUnitUnknown -> 0
-    JFifPixelsPerInch -> 1
-    JFifPixelsPerCentimeter -> 2
-  get = do
-    v <- getWord8
-    pure $ case v of
-      0 -> JFifUnitUnknown
-      1 -> JFifPixelsPerInch
-      2 -> JFifPixelsPerCentimeter
-      _ -> JFifUnitUnknown
-
-data JpgJFIFApp0 = JpgJFIFApp0
-  { _jfifUnit      :: !JFifUnit
-  , _jfifDpiX      :: !Word16
-  , _jfifDpiY      :: !Word16
-  , _jfifThumbnail :: !(Maybe {- (Image PixelRGB8) -} Int)
-  }
-  deriving Show
-
-instance Binary JpgJFIFApp0 where
-  get = do
-    sig <- getByteString 5
-    when (sig /= BC.pack "JFIF\0") $
-        fail "Invalid JFIF signature"
-    major <- getWord8
-    minor <- getWord8
-    when (major /= 1 && minor > 2) $
-        fail "Unrecognize JFIF version"
-    unit <- get
-    dpiX <- getWord16be
-    dpiY <- getWord16be
-    w <- getWord8
-    h <- getWord8
-    let pxCount = 3 * w * h
-    img <- case pxCount of
-      0 -> return Nothing
-      _ -> return Nothing
-    return $ JpgJFIFApp0
-        { _jfifUnit      = unit
-        , _jfifDpiX      = dpiX
-        , _jfifDpiY      = dpiY
-        , _jfifThumbnail = img
-        }
-
-
-  put jfif = do
-    putByteString $ BC.pack "JFIF\0" -- 5
-    putWord8 1                       -- 1 6
-    putWord8 2                       -- 1 7
-    put $ _jfifUnit jfif             -- 1 8
-    putWord16be $ _jfifDpiX jfif     -- 2 10
-    putWord16be $ _jfifDpiY jfif     -- 2 12
-    putWord8 0                       -- 1 13
-    putWord8 0                       -- 1 14
-
-{-Thumbnail width (tw) 	1 	Horizontal size of embedded JFIF thumbnail in pixels-}
-{-Thumbnail height (th) 	1 	Vertical size of embedded JFIF thumbnail in pixels-}
-{-Thumbnail data 	3 × tw × th 	Uncompressed 24 bit RGB raster thumbnail-}
-
-instance Binary AdobeTransform where
-  put v = case v of
-    AdobeUnknown -> putWord8 0
-    AdobeYCbCr -> putWord8 1
-    AdobeYCck -> putWord8 2
-
-  get = do
-    v <- getWord8
-    pure $ case v of
-      0 -> AdobeUnknown
-      1 -> AdobeYCbCr
-      2 -> AdobeYCck
-      _ -> AdobeUnknown
-
-instance Binary JpgAdobeApp14 where
-  get = do
-    let sig = BC.pack "Adobe"
-    fileSig <- getByteString 5
-    when (fileSig /= sig) $
-       fail "Invalid Adobe APP14 marker"
-    version <- getWord16be
-    when (version /= 100) $
-       fail $ "Invalid Adobe APP14 version " ++ show version
-    JpgAdobeApp14 version
-                  <$> getWord16be
-                  <*> getWord16be <*> get
-
-  put (JpgAdobeApp14 v f0 f1 t) = do
-    putByteString $ BC.pack "Adobe"
-    putWord16be v
-    putWord16be f0
-    putWord16be f1
-    put t
-
-
-data JpgFrameHeader = JpgFrameHeader
-    { jpgFrameHeaderLength   :: !Word16
-    , jpgSamplePrecision     :: !Word8
-    , jpgHeight              :: !Word16
-    , jpgWidth               :: !Word16
-    , jpgImageComponentCount :: !Word8
-    , jpgComponents          :: ![JpgComponent]
-    }
-    deriving Show
-
-
-instance SizeCalculable JpgFrameHeader where
-    calculateSize hdr = 2 + 1 + 2 + 2 + 1
-                      + sum [calculateSize c | c <- jpgComponents hdr]
-
-data JpgComponent = JpgComponent
-    { componentIdentifier       :: !Word8
-      -- | Stored with 4 bits
-    , horizontalSamplingFactor  :: !Word8
-      -- | Stored with 4 bits
-    , verticalSamplingFactor    :: !Word8
-    , quantizationTableDest     :: !Word8
-    }
-    deriving Show
-
-instance SizeCalculable JpgComponent where
-    calculateSize _ = 3
-
-data JpgImage = JpgImage { jpgFrame :: [JpgFrame] }
-    deriving Show
-
-data JpgScanSpecification = JpgScanSpecification
-    { componentSelector :: !Word8
-      -- | Encoded as 4 bits
-    , dcEntropyCodingTable :: !Word8
-      -- | Encoded as 4 bits
-    , acEntropyCodingTable :: !Word8
-
-    }
-    deriving Show
-
-instance SizeCalculable JpgScanSpecification where
-    calculateSize _ = 2
-
-data JpgScanHeader = JpgScanHeader
-    { scanLength :: !Word16
-    , scanComponentCount :: !Word8
-    , scans :: [JpgScanSpecification]
-
-      -- | (begin, end)
-    , spectralSelection    :: (Word8, Word8)
-
-      -- | Encoded as 4 bits
-    , successiveApproxHigh :: !Word8
-
-      -- | Encoded as 4 bits
-    , successiveApproxLow :: !Word8
-    }
-    deriving Show
-
-instance SizeCalculable JpgScanHeader where
-    calculateSize hdr = 2 + 1
-                      + sum [calculateSize c | c <- scans hdr]
-                      + 2
-                      + 1
-
-data JpgQuantTableSpec = JpgQuantTableSpec
-    { -- | Stored on 4 bits
-      quantPrecision     :: !Word8
-
-      -- | Stored on 4 bits
-    , quantDestination   :: !Word8
-
-    , quantTable         :: MacroBlock Int16
-    }
-    deriving Show
-
-class SizeCalculable a where
-    calculateSize :: a -> Int
-
--- | Type introduced only to avoid some typeclass overlapping
--- problem
-newtype TableList a = TableList [a]
-
-instance (SizeCalculable a, Binary a) => Binary (TableList a) where
-    put (TableList lst) = do
-        putWord16be . fromIntegral $ sum [calculateSize table | table <- lst] + 2
-        mapM_ put lst
-
-    get = TableList <$> (getWord16be >>= \s -> innerParse (fromIntegral s - 2))
-      where innerParse :: Int -> Get [a]
-            innerParse 0    = return []
-            innerParse size = do
-                onStart <- fromIntegral <$> bytesRead
-                table <- get
-                onEnd <- fromIntegral <$> bytesRead
-                (table :) <$> innerParse (size - (onEnd - onStart))
-
-instance SizeCalculable JpgQuantTableSpec where
-    calculateSize table =
-        1 + (fromIntegral (quantPrecision table) + 1) * 64
-
-instance Binary JpgQuantTableSpec where
-    put table = do
-        let precision = quantPrecision table
-        put4BitsOfEach precision (quantDestination table)
-        forM_ (VS.toList $ quantTable table) $ \coeff ->
-            if precision == 0 then putWord8 $ fromIntegral coeff
-                             else putWord16be $ fromIntegral coeff
-
-    get = do
-        (precision, dest) <- get4BitOfEach
-        coeffs <- replicateM 64 $ if precision == 0
-                then fromIntegral <$> getWord8
-                else fromIntegral <$> getWord16be
-        return JpgQuantTableSpec
-            { quantPrecision = precision
-            , quantDestination = dest
-            , quantTable = VS.fromListN 64 coeffs
-            }
-
-data JpgHuffmanTableSpec = JpgHuffmanTableSpec
-    { -- | 0 : DC, 1 : AC, stored on 4 bits
-      huffmanTableClass       :: !DctComponent
-      -- | Stored on 4 bits
-    , huffmanTableDest        :: !Word8
-
-    , huffSizes :: !(VU.Vector Word8)
-    , huffCodes :: !(V.Vector (VU.Vector Word8))
-    }
-    deriving Show
-
-instance SizeCalculable JpgHuffmanTableSpec where
-    calculateSize table = 1 + 16 + sum [fromIntegral e | e <- VU.toList $ huffSizes table]
-
-instance Binary JpgHuffmanTableSpec where
-    put table = do
-        let classVal = if huffmanTableClass table == DcComponent
-                          then 0 else 1
-        put4BitsOfEach classVal $ huffmanTableDest table
-        mapM_ put . VU.toList $ huffSizes table
-        forM_ [0 .. 15] $ \i ->
-            when (huffSizes table ! i /= 0)
-                 (let elements = VU.toList $ huffCodes table V.! i
-                  in mapM_ put elements)
-
-    get = do
-        (huffClass, huffDest) <- get4BitOfEach
-        sizes <- replicateM 16 getWord8
-        codes <- forM sizes $ \s ->
-            VU.replicateM (fromIntegral s) getWord8
-        return JpgHuffmanTableSpec
-            { huffmanTableClass =
-                if huffClass == 0 then DcComponent else AcComponent
-            , huffmanTableDest = huffDest
-            , huffSizes = VU.fromListN 16 sizes
-            , huffCodes = V.fromListN 16 codes
-            }
-
-instance Binary JpgImage where
-    put (JpgImage { jpgFrame = frames }) =
-        putWord8 0xFF >> putWord8 0xD8 >> mapM_ putFrame frames
-            >> putWord8 0xFF >> putWord8 0xD9
-
-    get = do
-        let startOfImageMarker = 0xD8
-            -- endOfImageMarker = 0xD9
-        checkMarker commonMarkerFirstByte startOfImageMarker
-        eatUntilCode
-        frames <- parseFrames
-        {-checkMarker commonMarkerFirstByte endOfImageMarker-}
-        return JpgImage { jpgFrame = frames }
-
-eatUntilCode :: Get ()
-eatUntilCode = do
-    code <- getWord8
-    unless (code == 0xFF) eatUntilCode
-
-takeCurrentFrame :: Get B.ByteString
-takeCurrentFrame = do
-    size <- getWord16be
-    getByteString (fromIntegral size - 2)
-
-putFrame :: JpgFrame -> Put
-putFrame (JpgAdobeAPP14 adobe) = 
-    put (JpgAppSegment 14) >> putWord16be 14 >> put adobe
-putFrame (JpgJFIF jfif) =
-    put (JpgAppSegment 0) >> putWord16be (14+2) >> put jfif
-putFrame (JpgExif exif) = putExif exif
-putFrame (JpgAppFrame appCode str) =
-    put (JpgAppSegment appCode) >> putWord16be (fromIntegral $ B.length str) >> put str
-putFrame (JpgExtension appCode str) =
-    put (JpgExtensionSegment appCode) >> putWord16be (fromIntegral $ B.length str) >> put str
-putFrame (JpgQuantTable tables) =
-    put JpgQuantizationTable >> put (TableList tables)
-putFrame (JpgHuffmanTable tables) =
-    put JpgHuffmanTableMarker >> put (TableList $ map fst tables)
-putFrame (JpgIntervalRestart size) =
-    put JpgRestartInterval >> put (RestartInterval size)
-putFrame (JpgScanBlob hdr blob) =
-    put JpgStartOfScan >> put hdr >> putLazyByteString blob
-putFrame (JpgScans kind hdr) =
-    put kind >> put hdr
-
---------------------------------------------------
-----            Serialization instances
---------------------------------------------------
-commonMarkerFirstByte :: Word8
-commonMarkerFirstByte = 0xFF
-
-checkMarker :: Word8 -> Word8 -> Get ()
-checkMarker b1 b2 = do
-    rb1 <- getWord8
-    rb2 <- getWord8
-    when (rb1 /= b1 || rb2 /= b2)
-         (fail "Invalid marker used")
-
-extractScanContent :: L.ByteString -> (L.ByteString, L.ByteString)
-extractScanContent str = aux 0
-  where maxi = fromIntegral $ L.length str - 1
-
-        aux n | n >= maxi = (str, L.empty)
-              | v == 0xFF && vNext /= 0 && not isReset = L.splitAt n str
-              | otherwise = aux (n + 1)
-            where v = str `L.index` n
-                  vNext = str `L.index` (n + 1)
-                  isReset = 0xD0 <= vNext && vNext <= 0xD7
-
-parseAdobe14 :: B.ByteString -> [JpgFrame] -> [JpgFrame]
-parseAdobe14 str lst = go where
-  go = case runGetStrict get str of
-    Left _err -> lst
-    Right app14 -> JpgAdobeAPP14 app14 : lst
-
--- | Parse JFIF or JFXX information. Right now only JFIF.
-parseJF__  :: B.ByteString -> [JpgFrame] -> [JpgFrame]
-parseJF__  str lst = go where
-  go = case runGetStrict get str of
-    Left _err -> lst
-    Right jfif -> JpgJFIF jfif : lst
-
-parseExif :: B.ByteString -> [JpgFrame] -> [JpgFrame]
-parseExif str lst 
-  | exifHeader `B.isPrefixOf` str = go
-  | otherwise = lst
-  where
-    exifHeader = BC.pack "Exif\0\0"
-    tiff = B.drop (B.length exifHeader) str
-    go = case runGetStrict (getP tiff) tiff of
-      Left _err -> lst
-      Right (_hdr :: TiffHeader, []) -> lst
-      Right (_hdr :: TiffHeader, ifds : _) -> JpgExif ifds : lst
-
-putExif :: [ImageFileDirectory] -> Put
-putExif ifds = putAll where
-  hdr = TiffHeader
-    { hdrEndianness = EndianBig
-    , hdrOffset = 8
-    }
-
-  ifdList = case partition (isInIFD0 . ifdIdentifier) ifds of
-    (ifd0, []) -> [ifd0]
-    (ifd0, ifdExif) -> [ifd0 <> pure exifOffsetIfd, ifdExif]
-  
-  exifBlob = runPut $ do
-    putByteString $ BC.pack "Exif\0\0"
-    putP BC.empty (hdr, ifdList)
-
-  putAll = do
-    put (JpgAppSegment 1)
-    putWord16be . fromIntegral $ L.length exifBlob + 2
-    putLazyByteString exifBlob
-
-parseFrames :: Get [JpgFrame]
-parseFrames = do
-    kind <- get
-    let parseNextFrame = do
-            word <- getWord8
-            when (word /= 0xFF) $ do
-                readedData <- bytesRead
-                fail $ "Invalid Frame marker (" ++ show word
-                     ++ ", bytes read : " ++ show readedData ++ ")"
-            parseFrames
-
-    case kind of
-        JpgEndOfImage -> return []
-        JpgAppSegment 0 ->
-            parseJF__ <$> takeCurrentFrame <*> parseNextFrame
-        JpgAppSegment 1 ->
-            parseExif <$> takeCurrentFrame <*> parseNextFrame
-        JpgAppSegment 14 ->
-            parseAdobe14 <$> takeCurrentFrame <*> parseNextFrame
-        JpgAppSegment c ->
-            (\frm lst -> JpgAppFrame c frm : lst) <$> takeCurrentFrame <*> parseNextFrame
-        JpgExtensionSegment c ->
-            (\frm lst -> JpgExtension c frm : lst) <$> takeCurrentFrame <*> parseNextFrame
-        JpgQuantizationTable ->
-            (\(TableList quants) lst -> JpgQuantTable quants : lst) <$> get <*> parseNextFrame
-        JpgRestartInterval ->
-            (\(RestartInterval i) lst -> JpgIntervalRestart i : lst) <$> get <*> parseNextFrame
-        JpgHuffmanTableMarker ->
-            (\(TableList huffTables) lst ->
-                    JpgHuffmanTable [(t, packHuffmanTree . buildPackedHuffmanTree $ huffCodes t) | t <- huffTables] : lst)
-                    <$> get <*> parseNextFrame
-        JpgStartOfScan ->
-            (\frm imgData ->
-                let (d, other) = extractScanContent imgData
-                in
-                case runGet parseFrames (L.drop 1 other) of
-                  Left _ -> [JpgScanBlob frm d]
-                  Right lst -> JpgScanBlob frm d : lst
-            ) <$> get <*> getRemainingLazyBytes
-
-        _ -> (\hdr lst -> JpgScans kind hdr : lst) <$> get <*> parseNextFrame
-
-buildPackedHuffmanTree :: V.Vector (VU.Vector Word8) -> HuffmanTree
-buildPackedHuffmanTree = buildHuffmanTree . map VU.toList . V.toList
-
-secondStartOfFrameByteOfKind :: JpgFrameKind -> Word8
-secondStartOfFrameByteOfKind = aux
-  where
-    aux JpgBaselineDCTHuffman = 0xC0
-    aux JpgExtendedSequentialDCTHuffman = 0xC1
-    aux JpgProgressiveDCTHuffman = 0xC2
-    aux JpgLosslessHuffman = 0xC3
-    aux JpgDifferentialSequentialDCTHuffman = 0xC5
-    aux JpgDifferentialProgressiveDCTHuffman = 0xC6
-    aux JpgDifferentialLosslessHuffman = 0xC7
-    aux JpgExtendedSequentialArithmetic = 0xC9
-    aux JpgProgressiveDCTArithmetic = 0xCA
-    aux JpgLosslessArithmetic = 0xCB
-    aux JpgHuffmanTableMarker = 0xC4
-    aux JpgDifferentialSequentialDCTArithmetic = 0xCD
-    aux JpgDifferentialProgressiveDCTArithmetic = 0xCE
-    aux JpgDifferentialLosslessArithmetic = 0xCF
-    aux JpgEndOfImage = 0xD9
-    aux JpgQuantizationTable = 0xDB
-    aux JpgStartOfScan = 0xDA
-    aux JpgRestartInterval = 0xDD
-    aux (JpgRestartIntervalEnd v) = v
-    aux (JpgAppSegment a) = (a + 0xE0)
-    aux (JpgExtensionSegment a) = a
-
-data JpgImageKind = BaseLineDCT | ProgressiveDCT
-
-instance Binary JpgFrameKind where
-    put v = putWord8 0xFF >> put (secondStartOfFrameByteOfKind v)
-    get = do
-        -- no lookahead :(
-        {-word <- getWord8-}
-        word2 <- getWord8
-        return $ case word2 of
-            0xC0 -> JpgBaselineDCTHuffman
-            0xC1 -> JpgExtendedSequentialDCTHuffman
-            0xC2 -> JpgProgressiveDCTHuffman
-            0xC3 -> JpgLosslessHuffman
-            0xC4 -> JpgHuffmanTableMarker
-            0xC5 -> JpgDifferentialSequentialDCTHuffman
-            0xC6 -> JpgDifferentialProgressiveDCTHuffman
-            0xC7 -> JpgDifferentialLosslessHuffman
-            0xC9 -> JpgExtendedSequentialArithmetic
-            0xCA -> JpgProgressiveDCTArithmetic
-            0xCB -> JpgLosslessArithmetic
-            0xCD -> JpgDifferentialSequentialDCTArithmetic
-            0xCE -> JpgDifferentialProgressiveDCTArithmetic
-            0xCF -> JpgDifferentialLosslessArithmetic
-            0xD9 -> JpgEndOfImage
-            0xDA -> JpgStartOfScan
-            0xDB -> JpgQuantizationTable
-            0xDD -> JpgRestartInterval
-            a | a >= 0xF0 -> JpgExtensionSegment a
-              | a >= 0xE0 -> JpgAppSegment (a - 0xE0)
-              | a >= 0xD0 && a <= 0xD7 -> JpgRestartIntervalEnd a
-              | otherwise -> error ("Invalid frame marker (" ++ show a ++ ")")
-
-put4BitsOfEach :: Word8 -> Word8 -> Put
-put4BitsOfEach a b = put $ (a `unsafeShiftL` 4) .|. b
-
-get4BitOfEach :: Get (Word8, Word8)
-get4BitOfEach = do
-    val <- get
-    return ((val `unsafeShiftR` 4) .&. 0xF, val .&. 0xF)
-
-newtype RestartInterval = RestartInterval Word16
-
-instance Binary RestartInterval where
-    put (RestartInterval i) = putWord16be 4 >> putWord16be i
-    get = do
-        size <- getWord16be
-        when (size /= 4) (fail "Invalid jpeg restart interval size")
-        RestartInterval <$> getWord16be
-
-instance Binary JpgComponent where
-    get = do
-        ident <- getWord8
-        (horiz, vert) <- get4BitOfEach
-        quantTableIndex <- getWord8
-        return JpgComponent
-            { componentIdentifier = ident
-            , horizontalSamplingFactor = horiz
-            , verticalSamplingFactor = vert
-            , quantizationTableDest = quantTableIndex
-            }
-    put v = do
-        put $ componentIdentifier v
-        put4BitsOfEach (horizontalSamplingFactor v) $ verticalSamplingFactor v
-        put $ quantizationTableDest v
-
-instance Binary JpgFrameHeader where
-    get = do
-        beginOffset <- fromIntegral <$> bytesRead
-        frmHLength <- getWord16be
-        samplePrec <- getWord8
-        h <- getWord16be
-        w <- getWord16be
-        compCount <- getWord8
-        components <- replicateM (fromIntegral compCount) get
-        endOffset <- fromIntegral <$> bytesRead
-        when (beginOffset - endOffset < fromIntegral frmHLength)
-             (skip $ fromIntegral frmHLength - (endOffset - beginOffset))
-        return JpgFrameHeader
-            { jpgFrameHeaderLength = frmHLength
-            , jpgSamplePrecision = samplePrec
-            , jpgHeight = h
-            , jpgWidth = w
-            , jpgImageComponentCount = compCount
-            , jpgComponents = components
-            }
-
-    put v = do
-        putWord16be $ jpgFrameHeaderLength v
-        putWord8    $ jpgSamplePrecision v
-        putWord16be $ jpgHeight v
-        putWord16be $ jpgWidth v
-        putWord8    $ jpgImageComponentCount v
-        mapM_ put   $ jpgComponents v
-
-instance Binary JpgScanSpecification where
-    put v = do
-        put $ componentSelector v
-        put4BitsOfEach (dcEntropyCodingTable v) $ acEntropyCodingTable v
-
-    get = do
-        compSel <- get
-        (dc, ac) <- get4BitOfEach
-        return JpgScanSpecification {
-            componentSelector = compSel
-          , dcEntropyCodingTable = dc
-          , acEntropyCodingTable = ac
-          }
-
-instance Binary JpgScanHeader where
-    get = do
-        thisScanLength <- getWord16be
-        compCount <- getWord8
-        comp <- replicateM (fromIntegral compCount) get
-        specBeg <- get
-        specEnd <- get
-        (approxHigh, approxLow) <- get4BitOfEach
-
-        return JpgScanHeader {
-            scanLength = thisScanLength,
-            scanComponentCount = compCount,
-            scans = comp,
-            spectralSelection = (specBeg, specEnd),
-            successiveApproxHigh = approxHigh,
-            successiveApproxLow = approxLow
-        }
-
-    put v = do
-        putWord16be $ scanLength v
-        putWord8 $ scanComponentCount v
-        mapM_ put $ scans v
-        putWord8 . fst $ spectralSelection v
-        putWord8 . snd $ spectralSelection v
-        put4BitsOfEach (successiveApproxHigh v) $ successiveApproxLow v
-
-{-# INLINE createEmptyMutableMacroBlock #-}
--- | Create a new macroblock with the good array size
-createEmptyMutableMacroBlock :: (Storable a, Num a) => ST s (MutableMacroBlock s a)
-createEmptyMutableMacroBlock = M.replicate 64 0
-
-printMacroBlock :: (Storable a, PrintfArg a)
-                => MutableMacroBlock s a -> ST s String
-printMacroBlock block = pLn 0
-    where pLn 64 = return "===============================\n"
-          pLn i = do
-              v <- block `M.unsafeRead` i
-              vn <- pLn (i+1)
-              return $ printf (if i `mod` 8 == 0 then "\n%5d " else "%5d ") v ++ vn
-
-printPureMacroBlock :: (Storable a, PrintfArg a) => MacroBlock a -> String
-printPureMacroBlock block = pLn 0
-    where pLn 64 = "===============================\n"
-          pLn i = str ++ pLn (i + 1)
-            where str | i `mod` 8 == 0 = printf "\n%5d " v
-                      | otherwise = printf "%5d" v
-                  v = block VS.! i
-
-
-{-# INLINE dctBlockSize #-}
-dctBlockSize :: Num a => a
-dctBlockSize = 8
src/Codec/Picture/Metadata.hs view
@@ -17,6 +17,7 @@                              , Value( .. )
                              , Elem( .. )
                              , SourceFormat( .. )
+                             , ColorSpace( .. )
 
                                -- * Functions
                              , Codec.Picture.Metadata.lookup
@@ -49,6 +50,7 @@ 
 
 import Control.DeepSeq( NFData( .. ) )
+import qualified Data.ByteString as B
 import qualified Data.Foldable as F
 
 import Codec.Picture.Metadata.Exif
@@ -61,7 +63,7 @@     Refl :: Equiv a a
 #endif
 
--- | Type describing the original file format of the ilfe.
+-- | Type describing the original file format of the file.
 data SourceFormat
   = SourceJpeg
   | SourceGif
@@ -75,6 +77,38 @@ instance NFData SourceFormat where
   rnf a = a `seq` ()
 
+-- | The same color values may result in slightly different colors on different
+-- devices. To get consistent colors accross multiple devices we need a way of
+-- mapping color values from a source device into their equivalents on the
+-- target device.
+--
+-- The solution is essentially to define, for each device, a family of mappings
+-- that convert between device colors and standard CIEXYZ or CIELAB colors. The
+-- collection of mappings for a device is known as the 'color-profile' of that
+-- device, and each color-profile can be thought of as describing a
+-- 'color-space'.
+--
+-- If we know the color-space of the input pixels, and the color space of the
+-- output device, then we can convert the colors in the image to their
+-- equivalents on the output device.
+--
+-- JuicyPixels does not parse color-profiles or attempt to perform color
+-- correction.
+--
+-- The following color space types are recognised:
+--
+--   * sRGB: Standard RGB color space.
+--   * Windows BMP color space: Color space information embedded within a V4
+--         Windows BMP file.
+--   * ICC profile: An ICC color profile.
+data ColorSpace = SRGB
+                | WindowsBitmapColorSpace !B.ByteString
+                | ICCProfile !B.ByteString
+                deriving (Eq, Show)
+
+instance NFData ColorSpace where
+  rnf v = v `seq` ()
+
 -- | Store various additional information about an image. If
 -- something is not recognized, it can be stored in an unknown tag.
 --
@@ -86,16 +120,20 @@ --          information can avoid the full decompression of the image.
 --          Ignored for image writing.
 --
---   * 'Height' Image height in pixels. Relyiung on the metadata for this
---          information can void the full decomrpession of the image.
+--   * 'Height' Image height in pixels. Relying on the metadata for this
+--          information can void the full decompression of the image.
 --          Ignored for image writing.
 --
+--   * 'ColorProfile' An unparsed ICC color profile. Currently only supported by
+--          the Bitmap format.
+--
 --   * 'Unknown' unlikely to be decoded, but usefull for metadata writing
 --
 --   * 'Exif' Exif tag and associated data.
 --
 data Keys a where
   Gamma       :: Keys Double
+  ColorSpace  :: Keys ColorSpace
   Format      :: Keys SourceFormat
   DpiX        :: Keys Word
   DpiY        :: Keys Word
@@ -140,6 +178,7 @@ keyEq :: Keys a -> Keys b -> Maybe (Equiv a b)
 keyEq a b = case (a, b) of
   (Gamma, Gamma) -> Just Refl
+  (ColorSpace, ColorSpace) -> Just Refl
   (DpiX, DpiX) -> Just Refl
   (DpiY, DpiY) -> Just Refl
   (Width, Width) -> Just Refl
src/Codec/Picture/Png.hs view
@@ -52,9 +52,9 @@ 
 import Codec.Picture.Types
 import Codec.Picture.Metadata
-import Codec.Picture.Png.Type
-import Codec.Picture.Png.Export
-import Codec.Picture.Png.Metadata
+import Codec.Picture.Png.Internal.Type
+import Codec.Picture.Png.Internal.Export
+import Codec.Picture.Png.Internal.Metadata
 import Codec.Picture.InternalHelper
 
 -- | Simple structure used to hold information about Adam7 deinterlacing.
− src/Codec/Picture/Png/Export.hs
@@ -1,268 +0,0 @@-{-# LANGUAGE CPP #-}
-{-# LANGUAGE FlexibleContexts #-}
-{-# LANGUAGE TypeSynonymInstances #-}
-{-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE ScopedTypeVariables #-}
--- | Module implementing a basic png export, no filtering is applyed, but
--- export at least valid images.
-module Codec.Picture.Png.Export( PngSavable( .. )
-                               , PngPaletteSaveable( .. )
-                               , writePng
-                               , encodeDynamicPng
-                               , writeDynamicPng
-                               ) where
-#if !MIN_VERSION_base(4,8,0)
-import Data.Monoid( mempty )
-#endif
-
-import Control.Monad( forM_ )
-import Control.Monad.ST( ST, runST )
-import Data.Bits( unsafeShiftR, (.&.) )
-import Data.Binary( encode )
-import Data.Monoid( (<>) )
-import Data.Word(Word8, Word16)
-import qualified Codec.Compression.Zlib as Z
-import qualified Data.ByteString as B
-import qualified Data.ByteString.Lazy as Lb
-
-import qualified Data.Vector.Storable as VS
-import qualified Data.Vector.Storable.Mutable as M
-
-import Codec.Picture.Types
-import Codec.Picture.Png.Type
-import Codec.Picture.Png.Metadata
-import Codec.Picture.Metadata( Metadatas )
-import Codec.Picture.VectorByteConversion( blitVector, toByteString )
-
--- | Encode a paletted image into a png if possible.
-class PngPaletteSaveable a where
-  -- | Encode a paletted image as a color indexed 8-bit PNG.
-  -- the palette must have between 1 and 256 values in it.
-  -- Accepts `PixelRGB8` and `PixelRGBA8` as palette pixel type
-  encodePalettedPng :: Image a -> Image Pixel8 -> Either String Lb.ByteString
-  encodePalettedPng = encodePalettedPngWithMetadata mempty
-
-  -- | Equivalent to 'encodePalettedPng' but allow writing of metadatas.
-  -- See `encodePngWithMetadata` for the details of encoded metadatas
-  -- Accepts `PixelRGB8` and `PixelRGBA8` as palette pixel type
-  encodePalettedPngWithMetadata :: Metadatas -> Image a -> Image Pixel8 -> Either String Lb.ByteString
-
-instance PngPaletteSaveable PixelRGB8 where
-  encodePalettedPngWithMetadata metas pal img
-      | w <= 0 || w > 256 || h /= 1 = Left "Invalid palette"
-      | VS.any isTooBig $ imageData img =
-          Left "Image contains indexes absent from the palette"
-      | otherwise = Right $ genericEncodePng (Just pal) Nothing PngIndexedColor metas img
-        where w = imageWidth pal
-              h = imageHeight pal
-              isTooBig v = fromIntegral v >= w
-
-instance PngPaletteSaveable PixelRGBA8 where
-  encodePalettedPngWithMetadata metas pal img
-      | w <= 0 || w > 256 || h /= 1 = Left "Invalid palette"
-      | VS.any isTooBig $ imageData img =
-          Left "Image contains indexes absent from the palette"
-      | otherwise = Right $ genericEncodePng (Just opaquePalette) (Just alphaPal) PngIndexedColor metas img
-    where
-      w = imageWidth pal
-      h = imageHeight pal
-      opaquePalette = dropAlphaLayer pal
-      alphaPal = imageData $ extractComponent PlaneAlpha pal
-      isTooBig v = fromIntegral v >= w
-
--- | Encode an image into a png if possible.
-class PngSavable a where
-    -- | Transform an image into a png encoded bytestring, ready
-    -- to be written as a file.
-    encodePng :: Image a -> Lb.ByteString
-    encodePng = encodePngWithMetadata mempty
-
-    -- | Encode a png using some metadatas. The following metadata keys will
-    -- be stored in a `tEXt` field :
-    --
-    --  * 'Codec.Picture.Metadata.Title'
-    --  * 'Codec.Picture.Metadata.Description'
-    --  * 'Codec.Picture.Metadata.Author'
-    --  * 'Codec.Picture.Metadata.Copyright'
-    --  * 'Codec.Picture.Metadata.Software'
-    --  * 'Codec.Picture.Metadata.Comment'
-    --  * 'Codec.Picture.Metadata.Disclaimer'
-    --  * 'Codec.Picture.Metadata.Source'
-    --  * 'Codec.Picture.Metadata.Warning'
-    --  * 'Codec.Picture.Metadata.Unknown' using the key present in the constructor.
-    -- 
-    -- the followings metadata will bes tored in the `gAMA` chunk.
-    --
-    --  * 'Codec.Picture.Metadata.Gamma'
-    --
-    -- The followings metadata will be stored in a `pHYs` chunk
-    --
-    --  * 'Codec.Picture.Metadata.DpiX'
-    --  * 'Codec.Picture.Metadata.DpiY' 
-    encodePngWithMetadata :: Metadatas -> Image a -> Lb.ByteString
-
-preparePngHeader :: Image a -> PngImageType -> Word8 -> PngIHdr
-preparePngHeader (Image { imageWidth = w, imageHeight = h }) imgType depth = PngIHdr
-  { width             = fromIntegral w
-  , height            = fromIntegral h
-  , bitDepth          = depth
-  , colourType        = imgType
-  , compressionMethod = 0
-  , filterMethod      = 0
-  , interlaceMethod   = PngNoInterlace
-  }
-
--- | Helper function to directly write an image as a png on disk.
-writePng :: (PngSavable pixel) => FilePath -> Image pixel -> IO ()
-writePng path img = Lb.writeFile path $ encodePng img
-
-endChunk :: PngRawChunk
-endChunk = mkRawChunk iENDSignature mempty
-
-prepareIDatChunk :: Lb.ByteString -> PngRawChunk
-prepareIDatChunk = mkRawChunk iDATSignature
-
-genericEncode16BitsPng :: forall px. (Pixel px, PixelBaseComponent px ~ Word16)
-                       => PngImageType -> Metadatas -> Image px -> Lb.ByteString
-genericEncode16BitsPng imgKind metas
-                 image@(Image { imageWidth = w, imageHeight = h, imageData = arr }) =
-  encode PngRawImage { header = hdr
-                     , chunks = encodeMetadatas metas 
-                              <> [ prepareIDatChunk imgEncodedData
-                                 , endChunk
-                                 ]
-                     }
-    where hdr = preparePngHeader image imgKind 16
-          zero = B.singleton 0
-          compCount = componentCount (undefined :: px)
-
-          lineSize = compCount * w
-          blitToByteString vec = blitVector vec 0 (lineSize * 2)
-          encodeLine line = blitToByteString $ runST $ do
-              finalVec <- M.new $ lineSize * 2 :: ST s (M.STVector s Word8)
-              let baseIndex = line * lineSize
-              forM_ [0 ..  lineSize - 1] $ \ix -> do
-                  let v = arr `VS.unsafeIndex` (baseIndex + ix)
-                      high = fromIntegral $ (v `unsafeShiftR` 8) .&. 0xFF
-                      low = fromIntegral $ v .&. 0xFF
-
-                  (finalVec `M.unsafeWrite` (ix * 2 + 0)) high
-                  (finalVec `M.unsafeWrite` (ix * 2 + 1)) low
-
-              VS.unsafeFreeze finalVec
-
-          imgEncodedData = Z.compress . Lb.fromChunks
-                        $ concat [[zero, encodeLine line] | line <- [0 .. h - 1]]
-
-preparePalette :: Palette -> PngRawChunk
-preparePalette pal = PngRawChunk
-  { chunkLength = fromIntegral $ imageWidth pal * 3
-  , chunkType   = pLTESignature
-  , chunkCRC    = pngComputeCrc [pLTESignature, binaryData]
-  , chunkData   = binaryData
-  }
-  where binaryData = Lb.fromChunks [toByteString $ imageData pal]
-
-preparePaletteAlpha :: VS.Vector Pixel8 -> PngRawChunk
-preparePaletteAlpha alphaPal = PngRawChunk
-  { chunkLength = fromIntegral $ VS.length alphaPal
-  , chunkType   = tRNSSignature
-  , chunkCRC    = pngComputeCrc [tRNSSignature, binaryData]
-  , chunkData   = binaryData
-  }
-  where binaryData = Lb.fromChunks [toByteString alphaPal]
-
-type PaletteAlpha = VS.Vector Pixel8
-
-genericEncodePng :: forall px. (Pixel px, PixelBaseComponent px ~ Word8)
-                 => Maybe Palette
-                 -> Maybe PaletteAlpha
-                 -> PngImageType -> Metadatas -> Image px
-                 -> Lb.ByteString
-genericEncodePng palette palAlpha imgKind metas
-                 image@(Image { imageWidth = w, imageHeight = h, imageData = arr }) =
-  encode PngRawImage { header = hdr
-                     , chunks = encodeMetadatas metas
-                              <> paletteChunk
-                              <> transpChunk
-                              <> [ prepareIDatChunk imgEncodedData
-                                 , endChunk
-                                 ]}
-  where
-    hdr = preparePngHeader image imgKind 8
-    zero = B.singleton 0
-    compCount = componentCount (undefined :: px)
-
-    paletteChunk = case palette of
-      Nothing -> []
-      Just p -> [preparePalette p]
-
-    transpChunk = case palAlpha of
-      Nothing -> []
-      Just p -> [preparePaletteAlpha p]
-
-    lineSize = compCount * w
-    encodeLine line = blitVector arr (line * lineSize) lineSize
-    imgEncodedData = Z.compress
-        . Lb.fromChunks
-        $ concat [[zero, encodeLine line] | line <- [0 .. h - 1]]
-
-instance PngSavable PixelRGBA8 where
-  encodePngWithMetadata = genericEncodePng Nothing Nothing PngTrueColourWithAlpha
-
-instance PngSavable PixelRGB8 where
-  encodePngWithMetadata = genericEncodePng Nothing Nothing PngTrueColour
-
-instance PngSavable Pixel8 where
-  encodePngWithMetadata = genericEncodePng Nothing Nothing PngGreyscale
-
-instance PngSavable PixelYA8 where
-  encodePngWithMetadata = genericEncodePng Nothing Nothing PngGreyscaleWithAlpha
-
-instance PngSavable PixelYA16 where
-  encodePngWithMetadata = genericEncode16BitsPng PngGreyscaleWithAlpha
-
-instance PngSavable Pixel16 where
-  encodePngWithMetadata = genericEncode16BitsPng PngGreyscale
-
-instance PngSavable PixelRGB16 where
-  encodePngWithMetadata = genericEncode16BitsPng PngTrueColour
-
-instance PngSavable PixelRGBA16 where
-  encodePngWithMetadata = genericEncode16BitsPng PngTrueColourWithAlpha
-
--- | Write a dynamic image in a .png image file if possible.
--- The same restriction as encodeDynamicPng apply.
-writeDynamicPng :: FilePath -> DynamicImage -> IO (Either String Bool)
-writeDynamicPng path img = case encodeDynamicPng img of
-        Left err -> return $ Left err
-        Right b  -> Lb.writeFile path b >> return (Right True)
-
--- | Encode a dynamic image in PNG if possible, supported images are:
---
---   * 'ImageY8'
---
---   * 'ImageY16'
---
---   * 'ImageYA8'
---
---   * 'ImageYA16'
---
---   * 'ImageRGB8'
---
---   * 'ImageRGB16'
---
---   * 'ImageRGBA8'
---
---   * 'ImageRGBA16'
---
-encodeDynamicPng :: DynamicImage -> Either String Lb.ByteString
-encodeDynamicPng (ImageRGB8 img) = Right $ encodePng img
-encodeDynamicPng (ImageRGBA8 img) = Right $ encodePng img
-encodeDynamicPng (ImageY8 img) = Right $ encodePng img
-encodeDynamicPng (ImageY16 img) = Right $ encodePng img
-encodeDynamicPng (ImageYA8 img) = Right $ encodePng img
-encodeDynamicPng (ImageYA16 img) = Right $ encodePng img
-encodeDynamicPng (ImageRGB16 img) = Right $ encodePng img
-encodeDynamicPng (ImageRGBA16 img) = Right $ encodePng img
-encodeDynamicPng _ = Left "Unsupported image format for PNG export"
+ src/Codec/Picture/Png/Internal/Export.hs view
@@ -0,0 +1,268 @@+{-# LANGUAGE CPP #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE TypeSynonymInstances #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+-- | Module implementing a basic png export, no filtering is applyed, but
+-- export at least valid images.
+module Codec.Picture.Png.Internal.Export( PngSavable( .. )
+                               , PngPaletteSaveable( .. )
+                               , writePng
+                               , encodeDynamicPng
+                               , writeDynamicPng
+                               ) where
+#if !MIN_VERSION_base(4,8,0)
+import Data.Monoid( mempty )
+#endif
+
+import Control.Monad( forM_ )
+import Control.Monad.ST( ST, runST )
+import Data.Bits( unsafeShiftR, (.&.) )
+import Data.Binary( encode )
+import Data.Monoid( (<>) )
+import Data.Word(Word8, Word16)
+import qualified Codec.Compression.Zlib as Z
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as Lb
+
+import qualified Data.Vector.Storable as VS
+import qualified Data.Vector.Storable.Mutable as M
+
+import Codec.Picture.Types
+import Codec.Picture.Png.Internal.Type
+import Codec.Picture.Png.Internal.Metadata
+import Codec.Picture.Metadata( Metadatas )
+import Codec.Picture.VectorByteConversion( blitVector, toByteString )
+
+-- | Encode a paletted image into a png if possible.
+class PngPaletteSaveable a where
+  -- | Encode a paletted image as a color indexed 8-bit PNG.
+  -- the palette must have between 1 and 256 values in it.
+  -- Accepts `PixelRGB8` and `PixelRGBA8` as palette pixel type
+  encodePalettedPng :: Image a -> Image Pixel8 -> Either String Lb.ByteString
+  encodePalettedPng = encodePalettedPngWithMetadata mempty
+
+  -- | Equivalent to 'encodePalettedPng' but allow writing of metadatas.
+  -- See `encodePngWithMetadata` for the details of encoded metadatas
+  -- Accepts `PixelRGB8` and `PixelRGBA8` as palette pixel type
+  encodePalettedPngWithMetadata :: Metadatas -> Image a -> Image Pixel8 -> Either String Lb.ByteString
+
+instance PngPaletteSaveable PixelRGB8 where
+  encodePalettedPngWithMetadata metas pal img
+      | w <= 0 || w > 256 || h /= 1 = Left "Invalid palette"
+      | VS.any isTooBig $ imageData img =
+          Left "Image contains indexes absent from the palette"
+      | otherwise = Right $ genericEncodePng (Just pal) Nothing PngIndexedColor metas img
+        where w = imageWidth pal
+              h = imageHeight pal
+              isTooBig v = fromIntegral v >= w
+
+instance PngPaletteSaveable PixelRGBA8 where
+  encodePalettedPngWithMetadata metas pal img
+      | w <= 0 || w > 256 || h /= 1 = Left "Invalid palette"
+      | VS.any isTooBig $ imageData img =
+          Left "Image contains indexes absent from the palette"
+      | otherwise = Right $ genericEncodePng (Just opaquePalette) (Just alphaPal) PngIndexedColor metas img
+    where
+      w = imageWidth pal
+      h = imageHeight pal
+      opaquePalette = dropAlphaLayer pal
+      alphaPal = imageData $ extractComponent PlaneAlpha pal
+      isTooBig v = fromIntegral v >= w
+
+-- | Encode an image into a png if possible.
+class PngSavable a where
+    -- | Transform an image into a png encoded bytestring, ready
+    -- to be written as a file.
+    encodePng :: Image a -> Lb.ByteString
+    encodePng = encodePngWithMetadata mempty
+
+    -- | Encode a png using some metadatas. The following metadata keys will
+    -- be stored in a `tEXt` field :
+    --
+    --  * 'Codec.Picture.Metadata.Title'
+    --  * 'Codec.Picture.Metadata.Description'
+    --  * 'Codec.Picture.Metadata.Author'
+    --  * 'Codec.Picture.Metadata.Copyright'
+    --  * 'Codec.Picture.Metadata.Software'
+    --  * 'Codec.Picture.Metadata.Comment'
+    --  * 'Codec.Picture.Metadata.Disclaimer'
+    --  * 'Codec.Picture.Metadata.Source'
+    --  * 'Codec.Picture.Metadata.Warning'
+    --  * 'Codec.Picture.Metadata.Unknown' using the key present in the constructor.
+    -- 
+    -- the followings metadata will bes tored in the `gAMA` chunk.
+    --
+    --  * 'Codec.Picture.Metadata.Gamma'
+    --
+    -- The followings metadata will be stored in a `pHYs` chunk
+    --
+    --  * 'Codec.Picture.Metadata.DpiX'
+    --  * 'Codec.Picture.Metadata.DpiY' 
+    encodePngWithMetadata :: Metadatas -> Image a -> Lb.ByteString
+
+preparePngHeader :: Image a -> PngImageType -> Word8 -> PngIHdr
+preparePngHeader (Image { imageWidth = w, imageHeight = h }) imgType depth = PngIHdr
+  { width             = fromIntegral w
+  , height            = fromIntegral h
+  , bitDepth          = depth
+  , colourType        = imgType
+  , compressionMethod = 0
+  , filterMethod      = 0
+  , interlaceMethod   = PngNoInterlace
+  }
+
+-- | Helper function to directly write an image as a png on disk.
+writePng :: (PngSavable pixel) => FilePath -> Image pixel -> IO ()
+writePng path img = Lb.writeFile path $ encodePng img
+
+endChunk :: PngRawChunk
+endChunk = mkRawChunk iENDSignature mempty
+
+prepareIDatChunk :: Lb.ByteString -> PngRawChunk
+prepareIDatChunk = mkRawChunk iDATSignature
+
+genericEncode16BitsPng :: forall px. (Pixel px, PixelBaseComponent px ~ Word16)
+                       => PngImageType -> Metadatas -> Image px -> Lb.ByteString
+genericEncode16BitsPng imgKind metas
+                 image@(Image { imageWidth = w, imageHeight = h, imageData = arr }) =
+  encode PngRawImage { header = hdr
+                     , chunks = encodeMetadatas metas 
+                              <> [ prepareIDatChunk imgEncodedData
+                                 , endChunk
+                                 ]
+                     }
+    where hdr = preparePngHeader image imgKind 16
+          zero = B.singleton 0
+          compCount = componentCount (undefined :: px)
+
+          lineSize = compCount * w
+          blitToByteString vec = blitVector vec 0 (lineSize * 2)
+          encodeLine line = blitToByteString $ runST $ do
+              finalVec <- M.new $ lineSize * 2 :: ST s (M.STVector s Word8)
+              let baseIndex = line * lineSize
+              forM_ [0 ..  lineSize - 1] $ \ix -> do
+                  let v = arr `VS.unsafeIndex` (baseIndex + ix)
+                      high = fromIntegral $ (v `unsafeShiftR` 8) .&. 0xFF
+                      low = fromIntegral $ v .&. 0xFF
+
+                  (finalVec `M.unsafeWrite` (ix * 2 + 0)) high
+                  (finalVec `M.unsafeWrite` (ix * 2 + 1)) low
+
+              VS.unsafeFreeze finalVec
+
+          imgEncodedData = Z.compress . Lb.fromChunks
+                        $ concat [[zero, encodeLine line] | line <- [0 .. h - 1]]
+
+preparePalette :: Palette -> PngRawChunk
+preparePalette pal = PngRawChunk
+  { chunkLength = fromIntegral $ imageWidth pal * 3
+  , chunkType   = pLTESignature
+  , chunkCRC    = pngComputeCrc [pLTESignature, binaryData]
+  , chunkData   = binaryData
+  }
+  where binaryData = Lb.fromChunks [toByteString $ imageData pal]
+
+preparePaletteAlpha :: VS.Vector Pixel8 -> PngRawChunk
+preparePaletteAlpha alphaPal = PngRawChunk
+  { chunkLength = fromIntegral $ VS.length alphaPal
+  , chunkType   = tRNSSignature
+  , chunkCRC    = pngComputeCrc [tRNSSignature, binaryData]
+  , chunkData   = binaryData
+  }
+  where binaryData = Lb.fromChunks [toByteString alphaPal]
+
+type PaletteAlpha = VS.Vector Pixel8
+
+genericEncodePng :: forall px. (Pixel px, PixelBaseComponent px ~ Word8)
+                 => Maybe Palette
+                 -> Maybe PaletteAlpha
+                 -> PngImageType -> Metadatas -> Image px
+                 -> Lb.ByteString
+genericEncodePng palette palAlpha imgKind metas
+                 image@(Image { imageWidth = w, imageHeight = h, imageData = arr }) =
+  encode PngRawImage { header = hdr
+                     , chunks = encodeMetadatas metas
+                              <> paletteChunk
+                              <> transpChunk
+                              <> [ prepareIDatChunk imgEncodedData
+                                 , endChunk
+                                 ]}
+  where
+    hdr = preparePngHeader image imgKind 8
+    zero = B.singleton 0
+    compCount = componentCount (undefined :: px)
+
+    paletteChunk = case palette of
+      Nothing -> []
+      Just p -> [preparePalette p]
+
+    transpChunk = case palAlpha of
+      Nothing -> []
+      Just p -> [preparePaletteAlpha p]
+
+    lineSize = compCount * w
+    encodeLine line = blitVector arr (line * lineSize) lineSize
+    imgEncodedData = Z.compress
+        . Lb.fromChunks
+        $ concat [[zero, encodeLine line] | line <- [0 .. h - 1]]
+
+instance PngSavable PixelRGBA8 where
+  encodePngWithMetadata = genericEncodePng Nothing Nothing PngTrueColourWithAlpha
+
+instance PngSavable PixelRGB8 where
+  encodePngWithMetadata = genericEncodePng Nothing Nothing PngTrueColour
+
+instance PngSavable Pixel8 where
+  encodePngWithMetadata = genericEncodePng Nothing Nothing PngGreyscale
+
+instance PngSavable PixelYA8 where
+  encodePngWithMetadata = genericEncodePng Nothing Nothing PngGreyscaleWithAlpha
+
+instance PngSavable PixelYA16 where
+  encodePngWithMetadata = genericEncode16BitsPng PngGreyscaleWithAlpha
+
+instance PngSavable Pixel16 where
+  encodePngWithMetadata = genericEncode16BitsPng PngGreyscale
+
+instance PngSavable PixelRGB16 where
+  encodePngWithMetadata = genericEncode16BitsPng PngTrueColour
+
+instance PngSavable PixelRGBA16 where
+  encodePngWithMetadata = genericEncode16BitsPng PngTrueColourWithAlpha
+
+-- | Write a dynamic image in a .png image file if possible.
+-- The same restriction as encodeDynamicPng apply.
+writeDynamicPng :: FilePath -> DynamicImage -> IO (Either String Bool)
+writeDynamicPng path img = case encodeDynamicPng img of
+        Left err -> return $ Left err
+        Right b  -> Lb.writeFile path b >> return (Right True)
+
+-- | Encode a dynamic image in PNG if possible, supported images are:
+--
+--   * 'ImageY8'
+--
+--   * 'ImageY16'
+--
+--   * 'ImageYA8'
+--
+--   * 'ImageYA16'
+--
+--   * 'ImageRGB8'
+--
+--   * 'ImageRGB16'
+--
+--   * 'ImageRGBA8'
+--
+--   * 'ImageRGBA16'
+--
+encodeDynamicPng :: DynamicImage -> Either String Lb.ByteString
+encodeDynamicPng (ImageRGB8 img) = Right $ encodePng img
+encodeDynamicPng (ImageRGBA8 img) = Right $ encodePng img
+encodeDynamicPng (ImageY8 img) = Right $ encodePng img
+encodeDynamicPng (ImageY16 img) = Right $ encodePng img
+encodeDynamicPng (ImageYA8 img) = Right $ encodePng img
+encodeDynamicPng (ImageYA16 img) = Right $ encodePng img
+encodeDynamicPng (ImageRGB16 img) = Right $ encodePng img
+encodeDynamicPng (ImageRGBA16 img) = Right $ encodePng img
+encodeDynamicPng _ = Left "Unsupported image format for PNG export"
+ src/Codec/Picture/Png/Internal/Metadata.hs view
@@ -0,0 +1,136 @@+{-# LANGUAGE CPP #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE OverloadedStrings #-}
+module Codec.Picture.Png.Internal.Metadata( extractMetadatas
+                                 , encodeMetadatas
+                                 ) where
+
+#if !MIN_VERSION_base(4,8,0)
+import Control.Applicative( (<$>), (<*>), pure )
+import Data.Monoid( Monoid, mempty )
+import Data.Foldable( foldMap )
+#endif
+
+import Data.Maybe( fromMaybe )
+import Data.Binary( Binary( get, put ), encode )
+import Data.Binary.Get( getLazyByteStringNul )
+import Data.Binary.Put( putLazyByteString, putWord8 )
+import qualified Data.ByteString.Lazy.Char8 as L
+import Data.Monoid( (<>) )
+
+import Codec.Picture.InternalHelper
+import qualified Codec.Picture.Metadata as Met
+import Codec.Picture.Metadata ( Metadatas
+                              , dotsPerMeterToDotPerInch
+                              , Elem( (:=>) ) )
+import Codec.Picture.Png.Internal.Type
+
+#if !MIN_VERSION_base(4,7,0)
+eitherFoldMap :: Monoid m => (a -> m) -> Either e a -> m
+eitherFoldMap f v = case v of
+  Left _ -> mempty
+  Right a -> f a
+#else
+eitherFoldMap :: Monoid m => (a -> m) -> Either e a -> m
+eitherFoldMap = foldMap
+#endif
+
+getGamma :: [L.ByteString] -> Metadatas
+getGamma [] = mempty
+getGamma (g:_) = eitherFoldMap unpackGamma $ runGet get g
+  where
+    unpackGamma gamma = Met.singleton Met.Gamma (getPngGamma gamma)
+
+getDpis :: [L.ByteString] -> Metadatas
+getDpis [] = mempty
+getDpis (b:_) = eitherFoldMap unpackPhys $ runGet get b
+  where
+    unpackPhys PngPhysicalDimension { pngUnit = PngUnitUnknown } =
+      Met.insert Met.DpiX 72 $ Met.singleton Met.DpiY 72
+    unpackPhys phy@PngPhysicalDimension { pngUnit = PngUnitMeter } =
+      Met.insert Met.DpiX dpx $ Met.singleton Met.DpiY dpy
+        where
+          dpx = dotsPerMeterToDotPerInch . fromIntegral $ pngDpiX phy
+          dpy = dotsPerMeterToDotPerInch . fromIntegral $ pngDpiY phy
+
+data PngText = PngText
+  { pngKeyword :: !L.ByteString
+  , pngData    :: !L.ByteString
+  }
+  deriving Show
+
+instance Binary PngText where
+  get = PngText <$> getLazyByteStringNul <*> getRemainingLazyBytes
+  put (PngText kw pdata) = do
+    putLazyByteString kw
+    putWord8 0
+    putLazyByteString pdata
+
+textToMetadata :: PngText -> Metadatas
+textToMetadata ptext = case pngKeyword ptext of
+  "Title" -> strValue Met.Title
+  "Author" -> strValue Met.Author
+  "Description" -> strValue Met.Description
+  "Copyright" -> strValue Met.Copyright
+  {-"Creation Time" -> strValue Creation-}
+  "Software" -> strValue Met.Software
+  "Disclaimer" -> strValue Met.Disclaimer
+  "Warning" -> strValue Met.Warning
+  "Source" -> strValue Met.Source
+  "Comment" -> strValue Met.Comment
+  other -> 
+    Met.singleton
+      (Met.Unknown $ L.unpack other)
+      (Met.String . L.unpack $ pngData ptext)
+  where
+    strValue k = Met.singleton k . L.unpack $ pngData ptext
+
+getTexts :: [L.ByteString] -> Metadatas
+getTexts = foldMap (eitherFoldMap textToMetadata . runGet get) where
+ 
+
+extractMetadatas :: PngRawImage -> Metadatas
+extractMetadatas img = getDpis (chunksOf pHYsSignature)
+                    <> getGamma (chunksOf gammaSignature)
+                    <> getTexts (chunksOf tEXtSignature)
+  where
+    chunksOf = chunksWithSig img
+
+encodePhysicalMetadata :: Metadatas -> [PngRawChunk]
+encodePhysicalMetadata metas = fromMaybe [] $ do
+  dx <- Met.lookup Met.DpiX metas
+  dy <- Met.lookup Met.DpiY metas
+  let to = fromIntegral . Met.dotPerInchToDotsPerMeter
+      dim = PngPhysicalDimension (to dx) (to dy) PngUnitMeter
+  pure [mkRawChunk pHYsSignature $ encode dim]
+
+encodeSingleMetadata :: Metadatas -> [PngRawChunk]
+encodeSingleMetadata = Met.foldMap go where
+  go :: Elem Met.Keys -> [PngRawChunk]
+  go v = case v of
+    Met.Exif _ :=> _ -> mempty
+    Met.DpiX :=> _ -> mempty
+    Met.DpiY :=> _ -> mempty
+    Met.Width :=> _ -> mempty
+    Met.Height :=> _ -> mempty
+    Met.Format :=> _ -> mempty
+    Met.Gamma       :=> g ->
+      pure $ mkRawChunk gammaSignature . encode $ PngGamma g
+    Met.ColorSpace  :=> _ -> mempty
+    Met.Title       :=> tx -> txt "Title" (L.pack tx)
+    Met.Description :=> tx -> txt "Description" (L.pack tx)
+    Met.Author      :=> tx -> txt "Author" (L.pack tx)
+    Met.Copyright   :=> tx -> txt "Copyright" (L.pack tx)
+    Met.Software    :=> tx -> txt "Software" (L.pack tx)
+    Met.Comment     :=> tx -> txt "Comment" (L.pack tx)
+    Met.Disclaimer  :=> tx -> txt "Disclaimer" (L.pack tx)
+    Met.Source      :=> tx -> txt "Source" (L.pack tx)
+    Met.Warning     :=> tx -> txt "Warning" (L.pack tx)
+    Met.Unknown k   :=> Met.String tx -> txt (L.pack k) (L.pack tx)
+    Met.Unknown _   :=> _ -> mempty
+
+  txt k c = pure . mkRawChunk tEXtSignature . encode $ PngText k c
+
+encodeMetadatas :: Metadatas -> [PngRawChunk]
+encodeMetadatas m = encodePhysicalMetadata m <> encodeSingleMetadata m
+
+ src/Codec/Picture/Png/Internal/Type.hs view
@@ -0,0 +1,450 @@+{-# LANGUAGE CPP #-}
+-- | Low level png module, you should import 'Codec.Picture.Png.Internal' instead.
+module Codec.Picture.Png.Internal.Type( PngIHdr( .. )
+                             , PngFilter( .. )
+                             , PngInterlaceMethod( .. )
+                             , PngPalette
+                             , PngImageType( .. )
+                             , PngPhysicalDimension( .. )
+                             , PngGamma( .. )
+                             , PngUnit( .. )
+                             , APngAnimationControl( .. )
+                             , APngFrameDisposal( .. )
+                             , APngBlendOp( .. )
+                             , APngFrameControl( .. )
+                             , parsePalette 
+                             , pngComputeCrc
+                             , pLTESignature
+                             , iDATSignature
+                             , iENDSignature
+                             , tRNSSignature
+                             , tEXtSignature
+                             , zTXtSignature
+                             , gammaSignature
+                             , pHYsSignature
+                             , animationControlSignature
+                             -- * Low level types
+                             , ChunkSignature
+                             , PngRawImage( .. )
+                             , PngChunk( .. )
+                             , PngRawChunk( .. )
+                             , PngLowLevel( .. )
+                             , chunksWithSig
+                             , mkRawChunk
+                             ) where
+
+#if !MIN_VERSION_base(4,8,0)
+import Control.Applicative( (<$>), (<*>), pure )
+#endif
+
+import Control.Monad( when, replicateM )
+import Data.Bits( xor, (.&.), unsafeShiftR )
+import Data.Binary( Binary(..), Get, get )
+import Data.Binary.Get( getWord8
+                      , getWord32be
+                      , getLazyByteString
+                      )
+import Data.Binary.Put( runPut
+                      , putWord8
+                      , putWord32be
+                      , putLazyByteString
+                      )
+import Data.Vector.Unboxed( Vector, fromListN, (!) )
+import qualified Data.Vector.Storable as V
+import Data.List( foldl' )
+import Data.Word( Word32, Word16, Word8 )
+import qualified Data.ByteString.Lazy as L
+import qualified Data.ByteString.Lazy.Char8 as LS
+
+import Codec.Picture.Types
+import Codec.Picture.InternalHelper
+
+--------------------------------------------------
+----            Types
+--------------------------------------------------
+
+-- | Value used to identify a png chunk, must be 4 bytes long.
+type ChunkSignature = L.ByteString
+
+-- | Generic header used in PNG images.
+data PngIHdr = PngIHdr
+    { width             :: !Word32       -- ^ Image width in number of pixel
+    , height            :: !Word32       -- ^ Image height in number of pixel
+    , bitDepth          :: !Word8        -- ^ Number of bit per sample
+    , colourType        :: !PngImageType -- ^ Kind of png image (greyscale, true color, indexed...)
+    , compressionMethod :: !Word8        -- ^ Compression method used
+    , filterMethod      :: !Word8        -- ^ Must be 0
+    , interlaceMethod   :: !PngInterlaceMethod   -- ^ If the image is interlaced (for progressive rendering)
+    }
+    deriving Show
+
+data PngUnit
+    = PngUnitUnknown -- ^ 0 value
+    | PngUnitMeter   -- ^ 1 value
+
+instance Binary PngUnit where
+  get = do
+    v <- getWord8
+    pure $ case v of
+      0 -> PngUnitUnknown
+      1 -> PngUnitMeter
+      _ -> PngUnitUnknown
+  
+  put v = case v of
+    PngUnitUnknown -> putWord8 0
+    PngUnitMeter -> putWord8 1
+
+data PngPhysicalDimension = PngPhysicalDimension
+    { pngDpiX     :: !Word32
+    , pngDpiY     :: !Word32
+    , pngUnit     :: !PngUnit
+    }
+
+instance Binary PngPhysicalDimension where
+  get = PngPhysicalDimension <$> getWord32be <*> getWord32be <*> get
+  put (PngPhysicalDimension dpx dpy unit) =
+    putWord32be dpx >> putWord32be dpy >> put unit
+
+newtype PngGamma = PngGamma { getPngGamma :: Double }
+
+instance Binary PngGamma where
+  get = PngGamma . (/ 100000) . fromIntegral <$> getWord32be
+  put = putWord32be . ceiling . (100000 *) . getPngGamma 
+
+data APngAnimationControl = APngAnimationControl
+    { animationFrameCount :: !Word32
+    , animationPlayCount  :: !Word32
+    }
+    deriving Show
+
+-- | Encoded in a Word8
+data APngFrameDisposal
+      -- | No disposal is done on this frame before rendering the
+      -- next; the contents of the output buffer are left as is. 
+      -- Has Value 0
+    = APngDisposeNone
+      -- | The frame's region of the output buffer is to be cleared
+      -- to fully transparent black before rendering the next frame. 
+      -- Has Value 1
+    | APngDisposeBackground
+      -- | the frame's region of the output buffer is to be reverted
+      -- to the previous contents before rendering the next frame.
+      -- Has Value 2
+    | APngDisposePrevious 
+    deriving Show
+
+-- | Encoded in a Word8
+data APngBlendOp
+      -- | Overwrite output buffer. has value '0'
+    = APngBlendSource
+      -- | Alpha blend to the output buffer. Has value '1'
+    | APngBlendOver
+    deriving Show
+
+data APngFrameControl = APngFrameControl
+    { frameSequenceNum      :: !Word32 -- ^ Starting from 0
+    , frameWidth            :: !Word32 -- ^ Width of the following frame
+    , frameHeight           :: !Word32 -- ^ Height of the following frame
+    , frameLeft             :: !Word32 -- X position where to render the frame.
+    , frameTop              :: !Word32 -- Y position where to render the frame.
+    , frameDelayNumerator   :: !Word16
+    , frameDelayDenuminator :: !Word16
+    , frameDisposal         :: !APngFrameDisposal
+    , frameBlending         :: !APngBlendOp
+    }
+    deriving Show
+
+-- | What kind of information is encoded in the IDAT section
+-- of the PngFile
+data PngImageType =
+      PngGreyscale
+    | PngTrueColour
+    | PngIndexedColor
+    | PngGreyscaleWithAlpha
+    | PngTrueColourWithAlpha
+    deriving Show
+
+-- | Raw parsed image which need to be decoded.
+data PngRawImage = PngRawImage
+    { header       :: PngIHdr
+    , chunks       :: [PngRawChunk]
+    }
+
+-- | Palette with indices beginning at 0 to elemcount - 1
+type PngPalette = Palette' PixelRGB8
+
+-- | Parse a palette from a png chunk.
+parsePalette :: PngRawChunk -> Either String PngPalette
+parsePalette plte
+ | chunkLength plte `mod` 3 /= 0 = Left "Invalid palette size"
+ | otherwise = Palette' pixelCount . V.fromListN (3 * pixelCount) <$> pixels
+    where pixelUnpacker = replicateM (fromIntegral pixelCount * 3) get
+          pixelCount = fromIntegral $ chunkLength plte `div` 3
+          pixels = runGet pixelUnpacker (chunkData plte)
+
+-- | Data structure during real png loading/parsing
+data PngRawChunk = PngRawChunk
+    { chunkLength :: Word32
+    , chunkType   :: ChunkSignature
+    , chunkCRC    :: Word32
+    , chunkData   :: L.ByteString
+    }
+
+mkRawChunk :: ChunkSignature -> L.ByteString -> PngRawChunk
+mkRawChunk sig binaryData = PngRawChunk
+  { chunkLength = fromIntegral $ L.length binaryData
+  , chunkType   = sig
+  , chunkCRC    = pngComputeCrc [sig, binaryData]
+  , chunkData   = binaryData
+  }
+
+-- | PNG chunk representing some extra information found in the parsed file.
+data PngChunk = PngChunk
+    { pngChunkData        :: L.ByteString  -- ^ The raw data inside the chunk
+    , pngChunkSignature   :: ChunkSignature -- ^ The name of the chunk.
+    }
+
+-- | Low level access to PNG information
+data PngLowLevel a = PngLowLevel
+    { pngImage  :: Image a      -- ^ The real uncompressed image
+    , pngChunks :: [PngChunk]   -- ^ List of raw chunk where some user data might be present.
+    }
+
+-- | The pixels value should be :
+-- +---+---+
+-- | c | b |
+-- +---+---+
+-- | a | x |
+-- +---+---+
+-- x being the current filtered pixel
+data PngFilter =
+    -- | Filt(x) = Orig(x), Recon(x) = Filt(x)
+      FilterNone
+    -- | Filt(x) = Orig(x) - Orig(a),     Recon(x) = Filt(x) + Recon(a)
+    | FilterSub
+    -- | Filt(x) = Orig(x) - Orig(b),     Recon(x) = Filt(x) + Recon(b)
+    | FilterUp
+    -- | Filt(x) = Orig(x) - floor((Orig(a) + Orig(b)) / 2),
+    -- Recon(x) = Filt(x) + floor((Recon(a) + Recon(b)) / 2)
+    | FilterAverage
+    -- | Filt(x) = Orig(x) - PaethPredictor(Orig(a), Orig(b), Orig(c)),
+    -- Recon(x) = Filt(x) + PaethPredictor(Recon(a), Recon(b), Recon(c))
+    | FilterPaeth
+    deriving (Enum, Show)
+
+-- | Different known interlace methods for PNG image
+data PngInterlaceMethod =
+      -- | No interlacing, basic data ordering, line by line
+      -- from left to right.
+      PngNoInterlace
+
+      -- | Use the Adam7 ordering, see `adam7Reordering`
+    | PngInterlaceAdam7
+    deriving (Enum, Show)
+
+--------------------------------------------------
+----            Instances
+--------------------------------------------------
+instance Binary PngFilter where
+    put = putWord8 . toEnum . fromEnum
+    get = getWord8 >>= \w -> case w of
+        0 -> return FilterNone
+        1 -> return FilterSub
+        2 -> return FilterUp
+        3 -> return FilterAverage
+        4 -> return FilterPaeth
+        _ -> fail "Invalid scanline filter"
+
+instance Binary PngRawImage where
+    put img = do
+        putLazyByteString pngSignature
+        put $ header img
+        mapM_ put $ chunks img
+
+    get = parseRawPngImage
+
+instance Binary PngRawChunk where
+    put chunk = do
+        putWord32be $ chunkLength chunk
+        putLazyByteString $ chunkType chunk
+        when (chunkLength chunk /= 0)
+             (putLazyByteString $ chunkData chunk)
+        putWord32be $ chunkCRC chunk
+
+    get = do
+        size <- getWord32be
+        chunkSig <- getLazyByteString (fromIntegral $ L.length iHDRSignature)
+        imgData <- if size == 0
+            then return L.empty
+            else getLazyByteString (fromIntegral size)
+        crc <- getWord32be
+
+        let computedCrc = pngComputeCrc [chunkSig, imgData]
+        when (computedCrc `xor` crc /= 0)
+             (fail $ "Invalid CRC : " ++ show computedCrc ++ ", "
+                                      ++ show crc)
+        return PngRawChunk {
+            chunkLength = size,
+            chunkData = imgData,
+            chunkCRC = crc,
+            chunkType = chunkSig
+        }
+
+instance Binary PngIHdr where
+    put hdr = do
+        putWord32be 13
+        let inner = runPut $ do
+                putLazyByteString iHDRSignature
+                putWord32be $ width hdr
+                putWord32be $ height hdr
+                putWord8    $ bitDepth hdr
+                put $ colourType hdr
+                put $ compressionMethod hdr
+                put $ filterMethod hdr
+                put $ interlaceMethod hdr
+            crc = pngComputeCrc [inner]
+        putLazyByteString inner
+        putWord32be crc
+
+    get = do
+        _size <- getWord32be
+        ihdrSig <- getLazyByteString (L.length iHDRSignature)
+        when (ihdrSig /= iHDRSignature)
+             (fail "Invalid PNG file, wrong ihdr")
+        w <- getWord32be
+        h <- getWord32be
+        depth <- get
+        colorType <- get
+        compression <- get
+        filtermethod <- get
+        interlace <- get
+        _crc <- getWord32be
+        return PngIHdr {
+            width = w,
+            height = h,
+            bitDepth = depth,
+            colourType = colorType,
+            compressionMethod = compression,
+            filterMethod = filtermethod,
+            interlaceMethod = interlace
+        }
+
+-- | Parse method for a png chunk, without decompression.
+parseChunks :: Get [PngRawChunk]
+parseChunks = do
+    chunk <- get
+
+    if chunkType chunk == iENDSignature
+       then return [chunk]
+       else (chunk:) <$> parseChunks
+
+
+instance Binary PngInterlaceMethod where
+    get = getWord8 >>= \w -> case w of
+        0 -> return PngNoInterlace
+        1 -> return PngInterlaceAdam7
+        _ -> fail "Invalid interlace method"
+
+    put PngNoInterlace    = putWord8 0
+    put PngInterlaceAdam7 = putWord8 1
+
+-- | Implementation of the get method for the PngRawImage,
+-- unpack raw data, without decompressing it.
+parseRawPngImage :: Get PngRawImage
+parseRawPngImage = do
+    sig <- getLazyByteString (L.length pngSignature)
+    when (sig /= pngSignature)
+         (fail "Invalid PNG file, signature broken")
+
+    ihdr <- get
+
+    chunkList <- parseChunks
+    return PngRawImage { header = ihdr, chunks = chunkList }
+
+--------------------------------------------------
+----            functions
+--------------------------------------------------
+
+-- | Signature signalling that the following data will be a png image
+-- in the png bit stream
+pngSignature :: ChunkSignature
+pngSignature = L.pack [137, 80, 78, 71, 13, 10, 26, 10]
+
+-- | Helper function to help pack signatures.
+signature :: String -> ChunkSignature
+signature = LS.pack 
+
+-- | Signature for the header chunk of png (must be the first)
+iHDRSignature :: ChunkSignature 
+iHDRSignature = signature "IHDR"
+
+-- | Signature for a palette chunk in the pgn file. Must
+-- occure before iDAT.
+pLTESignature :: ChunkSignature
+pLTESignature = signature "PLTE"
+
+-- | Signature for a data chuck (with image parts in it)
+iDATSignature :: ChunkSignature
+iDATSignature = signature "IDAT"
+
+-- | Signature for the last chunk of a png image, telling
+-- the end.
+iENDSignature :: ChunkSignature
+iENDSignature = signature "IEND"
+
+tRNSSignature :: ChunkSignature
+tRNSSignature = signature "tRNS"
+
+gammaSignature :: ChunkSignature
+gammaSignature = signature "gAMA"
+
+pHYsSignature :: ChunkSignature
+pHYsSignature = signature "pHYs"
+
+tEXtSignature :: ChunkSignature
+tEXtSignature = signature "tEXt"
+
+zTXtSignature :: ChunkSignature
+zTXtSignature = signature "zTXt"
+
+animationControlSignature :: ChunkSignature
+animationControlSignature = signature "acTL"
+
+instance Binary PngImageType where
+    put PngGreyscale = putWord8 0
+    put PngTrueColour = putWord8 2
+    put PngIndexedColor = putWord8 3
+    put PngGreyscaleWithAlpha = putWord8 4
+    put PngTrueColourWithAlpha = putWord8 6
+
+    get = get >>= imageTypeOfCode
+
+imageTypeOfCode :: Word8 -> Get PngImageType
+imageTypeOfCode 0 = return PngGreyscale
+imageTypeOfCode 2 = return PngTrueColour
+imageTypeOfCode 3 = return PngIndexedColor
+imageTypeOfCode 4 = return PngGreyscaleWithAlpha
+imageTypeOfCode 6 = return PngTrueColourWithAlpha
+imageTypeOfCode _ = fail "Invalid png color code"
+
+-- | From the Annex D of the png specification.
+pngCrcTable :: Vector Word32
+pngCrcTable = fromListN 256 [ foldl' updateCrcConstant c [zero .. 7] | c <- [0 .. 255] ]
+    where zero = 0 :: Int -- To avoid defaulting to Integer
+          updateCrcConstant c _ | c .&. 1 /= 0 = magicConstant `xor` (c `unsafeShiftR` 1)
+                                | otherwise = c `unsafeShiftR` 1
+          magicConstant = 0xedb88320 :: Word32
+
+-- | Compute the CRC of a raw buffer, as described in annex D of the PNG
+-- specification.
+pngComputeCrc :: [L.ByteString] -> Word32
+pngComputeCrc = (0xFFFFFFFF `xor`) . L.foldl' updateCrc 0xFFFFFFFF . L.concat
+    where updateCrc crc val =
+              let u32Val = fromIntegral val
+                  lutVal = pngCrcTable ! (fromIntegral ((crc `xor` u32Val) .&. 0xFF))
+              in lutVal `xor` (crc `unsafeShiftR` 8)
+
+chunksWithSig :: PngRawImage -> ChunkSignature -> [LS.ByteString]
+chunksWithSig rawImg sig =
+  [chunkData chunk | chunk <- chunks rawImg, chunkType chunk == sig]
+
− src/Codec/Picture/Png/Metadata.hs
@@ -1,135 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE OverloadedStrings #-}-module Codec.Picture.Png.Metadata( extractMetadatas-                                 , encodeMetadatas-                                 ) where--#if !MIN_VERSION_base(4,8,0)-import Control.Applicative( (<$>), (<*>), pure )-import Data.Monoid( Monoid, mempty )-import Data.Foldable( foldMap )-#endif--import Data.Maybe( fromMaybe )-import Data.Binary( Binary( get, put ), encode )-import Data.Binary.Get( getLazyByteStringNul )-import Data.Binary.Put( putLazyByteString, putWord8 )-import qualified Data.ByteString.Lazy.Char8 as L-import Data.Monoid( (<>) )--import Codec.Picture.InternalHelper-import qualified Codec.Picture.Metadata as Met-import Codec.Picture.Metadata ( Metadatas-                              , dotsPerMeterToDotPerInch-                              , Elem( (:=>) ) )-import Codec.Picture.Png.Type--#if !MIN_VERSION_base(4,7,0)-eitherFoldMap :: Monoid m => (a -> m) -> Either e a -> m-eitherFoldMap f v = case v of-  Left _ -> mempty-  Right a -> f a-#else-eitherFoldMap :: Monoid m => (a -> m) -> Either e a -> m-eitherFoldMap = foldMap-#endif--getGamma :: [L.ByteString] -> Metadatas-getGamma [] = mempty-getGamma (g:_) = eitherFoldMap unpackGamma $ runGet get g-  where-    unpackGamma gamma = Met.singleton Met.Gamma (getPngGamma gamma)--getDpis :: [L.ByteString] -> Metadatas-getDpis [] = mempty-getDpis (b:_) = eitherFoldMap unpackPhys $ runGet get b-  where-    unpackPhys PngPhysicalDimension { pngUnit = PngUnitUnknown } =-      Met.insert Met.DpiX 72 $ Met.singleton Met.DpiY 72-    unpackPhys phy@PngPhysicalDimension { pngUnit = PngUnitMeter } =-      Met.insert Met.DpiX dpx $ Met.singleton Met.DpiY dpy-        where-          dpx = dotsPerMeterToDotPerInch . fromIntegral $ pngDpiX phy-          dpy = dotsPerMeterToDotPerInch . fromIntegral $ pngDpiY phy--data PngText = PngText-  { pngKeyword :: !L.ByteString-  , pngData    :: !L.ByteString-  }-  deriving Show--instance Binary PngText where-  get = PngText <$> getLazyByteStringNul <*> getRemainingLazyBytes-  put (PngText kw pdata) = do-    putLazyByteString kw-    putWord8 0-    putLazyByteString pdata--textToMetadata :: PngText -> Metadatas-textToMetadata ptext = case pngKeyword ptext of-  "Title" -> strValue Met.Title-  "Author" -> strValue Met.Author-  "Description" -> strValue Met.Description-  "Copyright" -> strValue Met.Copyright-  {-"Creation Time" -> strValue Creation-}-  "Software" -> strValue Met.Software-  "Disclaimer" -> strValue Met.Disclaimer-  "Warning" -> strValue Met.Warning-  "Source" -> strValue Met.Source-  "Comment" -> strValue Met.Comment-  other -> -    Met.singleton-      (Met.Unknown $ L.unpack other)-      (Met.String . L.unpack $ pngData ptext)-  where-    strValue k = Met.singleton k . L.unpack $ pngData ptext--getTexts :: [L.ByteString] -> Metadatas-getTexts = foldMap (eitherFoldMap textToMetadata . runGet get) where- --extractMetadatas :: PngRawImage -> Metadatas-extractMetadatas img = getDpis (chunksOf pHYsSignature)-                    <> getGamma (chunksOf gammaSignature)-                    <> getTexts (chunksOf tEXtSignature)-  where-    chunksOf = chunksWithSig img--encodePhysicalMetadata :: Metadatas -> [PngRawChunk]-encodePhysicalMetadata metas = fromMaybe [] $ do-  dx <- Met.lookup Met.DpiX metas-  dy <- Met.lookup Met.DpiY metas-  let to = fromIntegral . Met.dotPerInchToDotsPerMeter-      dim = PngPhysicalDimension (to dx) (to dy) PngUnitMeter-  pure [mkRawChunk pHYsSignature $ encode dim]--encodeSingleMetadata :: Metadatas -> [PngRawChunk]-encodeSingleMetadata = Met.foldMap go where-  go :: Elem Met.Keys -> [PngRawChunk]-  go v = case v of-    Met.Exif _ :=> _ -> mempty-    Met.DpiX :=> _ -> mempty-    Met.DpiY :=> _ -> mempty-    Met.Width :=> _ -> mempty-    Met.Height :=> _ -> mempty-    Met.Format :=> _ -> mempty-    Met.Gamma       :=> g ->-      pure $ mkRawChunk gammaSignature . encode $ PngGamma g-    Met.Title       :=> tx -> txt "Title" (L.pack tx)-    Met.Description :=> tx -> txt "Description" (L.pack tx)-    Met.Author      :=> tx -> txt "Author" (L.pack tx)-    Met.Copyright   :=> tx -> txt "Copyright" (L.pack tx)-    Met.Software    :=> tx -> txt "Software" (L.pack tx)-    Met.Comment     :=> tx -> txt "Comment" (L.pack tx)-    Met.Disclaimer  :=> tx -> txt "Disclaimer" (L.pack tx)-    Met.Source      :=> tx -> txt "Source" (L.pack tx)-    Met.Warning     :=> tx -> txt "Warning" (L.pack tx)-    Met.Unknown k   :=> Met.String tx -> txt (L.pack k) (L.pack tx)-    Met.Unknown _   :=> _ -> mempty--  txt k c = pure . mkRawChunk tEXtSignature . encode $ PngText k c--encodeMetadatas :: Metadatas -> [PngRawChunk]-encodeMetadatas m = encodePhysicalMetadata m <> encodeSingleMetadata m-
− src/Codec/Picture/Png/Type.hs
@@ -1,450 +0,0 @@-{-# LANGUAGE CPP #-}
--- | Low level png module, you should import 'Codec.Picture.Png' instead.
-module Codec.Picture.Png.Type( PngIHdr( .. )
-                             , PngFilter( .. )
-                             , PngInterlaceMethod( .. )
-                             , PngPalette
-                             , PngImageType( .. )
-                             , PngPhysicalDimension( .. )
-                             , PngGamma( .. )
-                             , PngUnit( .. )
-                             , APngAnimationControl( .. )
-                             , APngFrameDisposal( .. )
-                             , APngBlendOp( .. )
-                             , APngFrameControl( .. )
-                             , parsePalette 
-                             , pngComputeCrc
-                             , pLTESignature
-                             , iDATSignature
-                             , iENDSignature
-                             , tRNSSignature
-                             , tEXtSignature
-                             , zTXtSignature
-                             , gammaSignature
-                             , pHYsSignature
-                             , animationControlSignature
-                             -- * Low level types
-                             , ChunkSignature
-                             , PngRawImage( .. )
-                             , PngChunk( .. )
-                             , PngRawChunk( .. )
-                             , PngLowLevel( .. )
-                             , chunksWithSig
-                             , mkRawChunk
-                             ) where
-
-#if !MIN_VERSION_base(4,8,0)
-import Control.Applicative( (<$>), (<*>), pure )
-#endif
-
-import Control.Monad( when, replicateM )
-import Data.Bits( xor, (.&.), unsafeShiftR )
-import Data.Binary( Binary(..), Get, get )
-import Data.Binary.Get( getWord8
-                      , getWord32be
-                      , getLazyByteString
-                      )
-import Data.Binary.Put( runPut
-                      , putWord8
-                      , putWord32be
-                      , putLazyByteString
-                      )
-import Data.Vector.Unboxed( Vector, fromListN, (!) )
-import qualified Data.Vector.Storable as V
-import Data.List( foldl' )
-import Data.Word( Word32, Word16, Word8 )
-import qualified Data.ByteString.Lazy as L
-import qualified Data.ByteString.Lazy.Char8 as LS
-
-import Codec.Picture.Types
-import Codec.Picture.InternalHelper
-
---------------------------------------------------
-----            Types
---------------------------------------------------
-
--- | Value used to identify a png chunk, must be 4 bytes long.
-type ChunkSignature = L.ByteString
-
--- | Generic header used in PNG images.
-data PngIHdr = PngIHdr
-    { width             :: !Word32       -- ^ Image width in number of pixel
-    , height            :: !Word32       -- ^ Image height in number of pixel
-    , bitDepth          :: !Word8        -- ^ Number of bit per sample
-    , colourType        :: !PngImageType -- ^ Kind of png image (greyscale, true color, indexed...)
-    , compressionMethod :: !Word8        -- ^ Compression method used
-    , filterMethod      :: !Word8        -- ^ Must be 0
-    , interlaceMethod   :: !PngInterlaceMethod   -- ^ If the image is interlaced (for progressive rendering)
-    }
-    deriving Show
-
-data PngUnit
-    = PngUnitUnknown -- ^ 0 value
-    | PngUnitMeter   -- ^ 1 value
-
-instance Binary PngUnit where
-  get = do
-    v <- getWord8
-    pure $ case v of
-      0 -> PngUnitUnknown
-      1 -> PngUnitMeter
-      _ -> PngUnitUnknown
-  
-  put v = case v of
-    PngUnitUnknown -> putWord8 0
-    PngUnitMeter -> putWord8 1
-
-data PngPhysicalDimension = PngPhysicalDimension
-    { pngDpiX     :: !Word32
-    , pngDpiY     :: !Word32
-    , pngUnit     :: !PngUnit
-    }
-
-instance Binary PngPhysicalDimension where
-  get = PngPhysicalDimension <$> getWord32be <*> getWord32be <*> get
-  put (PngPhysicalDimension dpx dpy unit) =
-    putWord32be dpx >> putWord32be dpy >> put unit
-
-newtype PngGamma = PngGamma { getPngGamma :: Double }
-
-instance Binary PngGamma where
-  get = PngGamma . (/ 100000) . fromIntegral <$> getWord32be
-  put = putWord32be . ceiling . (100000 *) . getPngGamma 
-
-data APngAnimationControl = APngAnimationControl
-    { animationFrameCount :: !Word32
-    , animationPlayCount  :: !Word32
-    }
-    deriving Show
-
--- | Encoded in a Word8
-data APngFrameDisposal
-      -- | No disposal is done on this frame before rendering the
-      -- next; the contents of the output buffer are left as is. 
-      -- Has Value 0
-    = APngDisposeNone
-      -- | The frame's region of the output buffer is to be cleared
-      -- to fully transparent black before rendering the next frame. 
-      -- Has Value 1
-    | APngDisposeBackground
-      -- | the frame's region of the output buffer is to be reverted
-      -- to the previous contents before rendering the next frame.
-      -- Has Value 2
-    | APngDisposePrevious 
-    deriving Show
-
--- | Encoded in a Word8
-data APngBlendOp
-      -- | Overwrite output buffer. has value '0'
-    = APngBlendSource
-      -- | Alpha blend to the output buffer. Has value '1'
-    | APngBlendOver
-    deriving Show
-
-data APngFrameControl = APngFrameControl
-    { frameSequenceNum      :: !Word32 -- ^ Starting from 0
-    , frameWidth            :: !Word32 -- ^ Width of the following frame
-    , frameHeight           :: !Word32 -- ^ Height of the following frame
-    , frameLeft             :: !Word32 -- X position where to render the frame.
-    , frameTop              :: !Word32 -- Y position where to render the frame.
-    , frameDelayNumerator   :: !Word16
-    , frameDelayDenuminator :: !Word16
-    , frameDisposal         :: !APngFrameDisposal
-    , frameBlending         :: !APngBlendOp
-    }
-    deriving Show
-
--- | What kind of information is encoded in the IDAT section
--- of the PngFile
-data PngImageType =
-      PngGreyscale
-    | PngTrueColour
-    | PngIndexedColor
-    | PngGreyscaleWithAlpha
-    | PngTrueColourWithAlpha
-    deriving Show
-
--- | Raw parsed image which need to be decoded.
-data PngRawImage = PngRawImage
-    { header       :: PngIHdr
-    , chunks       :: [PngRawChunk]
-    }
-
--- | Palette with indices beginning at 0 to elemcount - 1
-type PngPalette = Palette' PixelRGB8
-
--- | Parse a palette from a png chunk.
-parsePalette :: PngRawChunk -> Either String PngPalette
-parsePalette plte
- | chunkLength plte `mod` 3 /= 0 = Left "Invalid palette size"
- | otherwise = Palette' pixelCount . V.fromListN (3 * pixelCount) <$> pixels
-    where pixelUnpacker = replicateM (fromIntegral pixelCount * 3) get
-          pixelCount = fromIntegral $ chunkLength plte `div` 3
-          pixels = runGet pixelUnpacker (chunkData plte)
-
--- | Data structure during real png loading/parsing
-data PngRawChunk = PngRawChunk
-    { chunkLength :: Word32
-    , chunkType   :: ChunkSignature
-    , chunkCRC    :: Word32
-    , chunkData   :: L.ByteString
-    }
-
-mkRawChunk :: ChunkSignature -> L.ByteString -> PngRawChunk
-mkRawChunk sig binaryData = PngRawChunk
-  { chunkLength = fromIntegral $ L.length binaryData
-  , chunkType   = sig
-  , chunkCRC    = pngComputeCrc [sig, binaryData]
-  , chunkData   = binaryData
-  }
-
--- | PNG chunk representing some extra information found in the parsed file.
-data PngChunk = PngChunk
-    { pngChunkData        :: L.ByteString  -- ^ The raw data inside the chunk
-    , pngChunkSignature   :: ChunkSignature -- ^ The name of the chunk.
-    }
-
--- | Low level access to PNG information
-data PngLowLevel a = PngLowLevel
-    { pngImage  :: Image a      -- ^ The real uncompressed image
-    , pngChunks :: [PngChunk]   -- ^ List of raw chunk where some user data might be present.
-    }
-
--- | The pixels value should be :
--- +---+---+
--- | c | b |
--- +---+---+
--- | a | x |
--- +---+---+
--- x being the current filtered pixel
-data PngFilter =
-    -- | Filt(x) = Orig(x), Recon(x) = Filt(x)
-      FilterNone
-    -- | Filt(x) = Orig(x) - Orig(a),     Recon(x) = Filt(x) + Recon(a)
-    | FilterSub
-    -- | Filt(x) = Orig(x) - Orig(b),     Recon(x) = Filt(x) + Recon(b)
-    | FilterUp
-    -- | Filt(x) = Orig(x) - floor((Orig(a) + Orig(b)) / 2),
-    -- Recon(x) = Filt(x) + floor((Recon(a) + Recon(b)) / 2)
-    | FilterAverage
-    -- | Filt(x) = Orig(x) - PaethPredictor(Orig(a), Orig(b), Orig(c)),
-    -- Recon(x) = Filt(x) + PaethPredictor(Recon(a), Recon(b), Recon(c))
-    | FilterPaeth
-    deriving (Enum, Show)
-
--- | Different known interlace methods for PNG image
-data PngInterlaceMethod =
-      -- | No interlacing, basic data ordering, line by line
-      -- from left to right.
-      PngNoInterlace
-
-      -- | Use the Adam7 ordering, see `adam7Reordering`
-    | PngInterlaceAdam7
-    deriving (Enum, Show)
-
---------------------------------------------------
-----            Instances
---------------------------------------------------
-instance Binary PngFilter where
-    put = putWord8 . toEnum . fromEnum
-    get = getWord8 >>= \w -> case w of
-        0 -> return FilterNone
-        1 -> return FilterSub
-        2 -> return FilterUp
-        3 -> return FilterAverage
-        4 -> return FilterPaeth
-        _ -> fail "Invalid scanline filter"
-
-instance Binary PngRawImage where
-    put img = do
-        putLazyByteString pngSignature
-        put $ header img
-        mapM_ put $ chunks img
-
-    get = parseRawPngImage
-
-instance Binary PngRawChunk where
-    put chunk = do
-        putWord32be $ chunkLength chunk
-        putLazyByteString $ chunkType chunk
-        when (chunkLength chunk /= 0)
-             (putLazyByteString $ chunkData chunk)
-        putWord32be $ chunkCRC chunk
-
-    get = do
-        size <- getWord32be
-        chunkSig <- getLazyByteString (fromIntegral $ L.length iHDRSignature)
-        imgData <- if size == 0
-            then return L.empty
-            else getLazyByteString (fromIntegral size)
-        crc <- getWord32be
-
-        let computedCrc = pngComputeCrc [chunkSig, imgData]
-        when (computedCrc `xor` crc /= 0)
-             (fail $ "Invalid CRC : " ++ show computedCrc ++ ", "
-                                      ++ show crc)
-        return PngRawChunk {
-            chunkLength = size,
-            chunkData = imgData,
-            chunkCRC = crc,
-            chunkType = chunkSig
-        }
-
-instance Binary PngIHdr where
-    put hdr = do
-        putWord32be 13
-        let inner = runPut $ do
-                putLazyByteString iHDRSignature
-                putWord32be $ width hdr
-                putWord32be $ height hdr
-                putWord8    $ bitDepth hdr
-                put $ colourType hdr
-                put $ compressionMethod hdr
-                put $ filterMethod hdr
-                put $ interlaceMethod hdr
-            crc = pngComputeCrc [inner]
-        putLazyByteString inner
-        putWord32be crc
-
-    get = do
-        _size <- getWord32be
-        ihdrSig <- getLazyByteString (L.length iHDRSignature)
-        when (ihdrSig /= iHDRSignature)
-             (fail "Invalid PNG file, wrong ihdr")
-        w <- getWord32be
-        h <- getWord32be
-        depth <- get
-        colorType <- get
-        compression <- get
-        filtermethod <- get
-        interlace <- get
-        _crc <- getWord32be
-        return PngIHdr {
-            width = w,
-            height = h,
-            bitDepth = depth,
-            colourType = colorType,
-            compressionMethod = compression,
-            filterMethod = filtermethod,
-            interlaceMethod = interlace
-        }
-
--- | Parse method for a png chunk, without decompression.
-parseChunks :: Get [PngRawChunk]
-parseChunks = do
-    chunk <- get
-
-    if chunkType chunk == iENDSignature
-       then return [chunk]
-       else (chunk:) <$> parseChunks
-
-
-instance Binary PngInterlaceMethod where
-    get = getWord8 >>= \w -> case w of
-        0 -> return PngNoInterlace
-        1 -> return PngInterlaceAdam7
-        _ -> fail "Invalid interlace method"
-
-    put PngNoInterlace    = putWord8 0
-    put PngInterlaceAdam7 = putWord8 1
-
--- | Implementation of the get method for the PngRawImage,
--- unpack raw data, without decompressing it.
-parseRawPngImage :: Get PngRawImage
-parseRawPngImage = do
-    sig <- getLazyByteString (L.length pngSignature)
-    when (sig /= pngSignature)
-         (fail "Invalid PNG file, signature broken")
-
-    ihdr <- get
-
-    chunkList <- parseChunks
-    return PngRawImage { header = ihdr, chunks = chunkList }
-
---------------------------------------------------
-----            functions
---------------------------------------------------
-
--- | Signature signalling that the following data will be a png image
--- in the png bit stream
-pngSignature :: ChunkSignature
-pngSignature = L.pack [137, 80, 78, 71, 13, 10, 26, 10]
-
--- | Helper function to help pack signatures.
-signature :: String -> ChunkSignature
-signature = LS.pack 
-
--- | Signature for the header chunk of png (must be the first)
-iHDRSignature :: ChunkSignature 
-iHDRSignature = signature "IHDR"
-
--- | Signature for a palette chunk in the pgn file. Must
--- occure before iDAT.
-pLTESignature :: ChunkSignature
-pLTESignature = signature "PLTE"
-
--- | Signature for a data chuck (with image parts in it)
-iDATSignature :: ChunkSignature
-iDATSignature = signature "IDAT"
-
--- | Signature for the last chunk of a png image, telling
--- the end.
-iENDSignature :: ChunkSignature
-iENDSignature = signature "IEND"
-
-tRNSSignature :: ChunkSignature
-tRNSSignature = signature "tRNS"
-
-gammaSignature :: ChunkSignature
-gammaSignature = signature "gAMA"
-
-pHYsSignature :: ChunkSignature
-pHYsSignature = signature "pHYs"
-
-tEXtSignature :: ChunkSignature
-tEXtSignature = signature "tEXt"
-
-zTXtSignature :: ChunkSignature
-zTXtSignature = signature "zTXt"
-
-animationControlSignature :: ChunkSignature
-animationControlSignature = signature "acTL"
-
-instance Binary PngImageType where
-    put PngGreyscale = putWord8 0
-    put PngTrueColour = putWord8 2
-    put PngIndexedColor = putWord8 3
-    put PngGreyscaleWithAlpha = putWord8 4
-    put PngTrueColourWithAlpha = putWord8 6
-
-    get = get >>= imageTypeOfCode
-
-imageTypeOfCode :: Word8 -> Get PngImageType
-imageTypeOfCode 0 = return PngGreyscale
-imageTypeOfCode 2 = return PngTrueColour
-imageTypeOfCode 3 = return PngIndexedColor
-imageTypeOfCode 4 = return PngGreyscaleWithAlpha
-imageTypeOfCode 6 = return PngTrueColourWithAlpha
-imageTypeOfCode _ = fail "Invalid png color code"
-
--- | From the Annex D of the png specification.
-pngCrcTable :: Vector Word32
-pngCrcTable = fromListN 256 [ foldl' updateCrcConstant c [zero .. 7] | c <- [0 .. 255] ]
-    where zero = 0 :: Int -- To avoid defaulting to Integer
-          updateCrcConstant c _ | c .&. 1 /= 0 = magicConstant `xor` (c `unsafeShiftR` 1)
-                                | otherwise = c `unsafeShiftR` 1
-          magicConstant = 0xedb88320 :: Word32
-
--- | Compute the CRC of a raw buffer, as described in annex D of the PNG
--- specification.
-pngComputeCrc :: [L.ByteString] -> Word32
-pngComputeCrc = (0xFFFFFFFF `xor`) . L.foldl' updateCrc 0xFFFFFFFF . L.concat
-    where updateCrc crc val =
-              let u32Val = fromIntegral val
-                  lutVal = pngCrcTable ! (fromIntegral ((crc `xor` u32Val) .&. 0xFF))
-              in lutVal `xor` (crc `unsafeShiftR` 8)
-
-chunksWithSig :: PngRawImage -> ChunkSignature -> [LS.ByteString]
-chunksWithSig rawImg sig =
-  [chunkData chunk | chunk <- chunks rawImg, chunkType chunk == sig]
-
src/Codec/Picture/Saving.hs view
@@ -120,7 +120,7 @@ -- of 'decodeImage' for jpeg encoding
 -- Save Y or YCbCr Jpeg only, all other colorspaces are converted.
 -- To save a RGB or CMYK JPEG file, use the
--- 'Codec.Picture.Jpg.encodeDirectJpegAtQualityWithMetadata' function
+-- 'Codec.Picture.Jpg.Internal.encodeDirectJpegAtQualityWithMetadata' function
 imageToJpg :: Int -> DynamicImage -> L.ByteString
 imageToJpg quality dynImage =
     let encodeAtQuality = encodeJpegAtQuality (fromIntegral quality)
src/Codec/Picture/Tiff.hs view
@@ -63,9 +63,9 @@ import Codec.Picture.InternalHelper
 import Codec.Picture.BitWriter
 import Codec.Picture.Types
-import Codec.Picture.Gif.LZW
-import Codec.Picture.Tiff.Types
-import Codec.Picture.Tiff.Metadata
+import Codec.Picture.Gif.Internal.LZW
+import Codec.Picture.Tiff.Internal.Types
+import Codec.Picture.Tiff.Internal.Metadata
 import Codec.Picture.VectorByteConversion( toByteString )
 
 data TiffInfo = TiffInfo
+ src/Codec/Picture/Tiff/Internal/Metadata.hs view
@@ -0,0 +1,237 @@+{-# LANGUAGE CPP #-}
+module Codec.Picture.Tiff.Internal.Metadata
+    ( extractTiffMetadata
+    , encodeTiffStringMetadata
+    , exifOffsetIfd
+    ) where
+
+#if !MIN_VERSION_base(4,8,0)
+import Data.Monoid( mempty )
+import Data.Foldable( foldMap )
+import Control.Applicative( (<$>) )
+#endif
+
+import Data.Bits( unsafeShiftL, (.|.) )
+import Data.Foldable( find )
+import Data.List( sortBy )
+import Data.Function( on )
+import qualified Data.Foldable as F
+import Data.Monoid( (<>) )
+import Codec.Picture.Metadata( Metadatas )
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Char8 as BC
+import qualified Codec.Picture.Metadata as Met
+import qualified Data.Vector.Generic as V
+import Codec.Picture.Tiff.Internal.Types
+import Codec.Picture.Metadata( extractExifMetas )
+import Codec.Picture.Metadata.Exif
+
+exifOffsetIfd :: ImageFileDirectory
+exifOffsetIfd = ImageFileDirectory
+  { ifdIdentifier = TagExifOffset
+  , ifdCount = 1
+  , ifdType = TypeLong
+  , ifdOffset = 0
+  , ifdExtended = ExifNone
+  }
+
+typeOfData :: ExifData -> IfdType
+typeOfData d = case d of
+  ExifNone -> error "Impossible - typeOfData : ExifNone"
+  ExifIFD _exifs -> error "Impossible - typeOfData : ExifIFD"
+  ExifLong _l -> TypeLong
+  ExifLongs _l -> TypeLong
+  ExifShort _s -> TypeShort
+  ExifShorts _s -> TypeShort
+  ExifString _str -> TypeAscii
+  ExifUndefined _undef -> TypeUndefined
+  ExifRational _r1 _r2 -> TypeRational
+  ExifSignedRational _sr1 _sr2 -> TypeSignedRational
+
+makeIfd :: ExifTag -> ExifData -> ImageFileDirectory
+makeIfd t (ExifShort v) = ImageFileDirectory
+  { ifdIdentifier = t
+  , ifdType = TypeShort
+  , ifdCount = 1
+  , ifdOffset = fromIntegral v `unsafeShiftL` 16
+  , ifdExtended = ExifNone
+  }
+makeIfd t (ExifLong v) = ImageFileDirectory 
+  { ifdIdentifier = t
+  , ifdType = TypeLong
+  , ifdCount = 1
+  , ifdOffset = fromIntegral v
+  , ifdExtended = ExifNone
+  }
+makeIfd t d@(ExifShorts v)
+  | size == 2 = ImageFileDirectory
+    { ifdIdentifier = t
+    , ifdType = TypeShort
+    , ifdCount = 2
+    , ifdOffset = combined
+    , ifdExtended = ExifNone
+    }
+  | otherwise = ImageFileDirectory
+    { ifdIdentifier = t
+    , ifdType = TypeShort
+    , ifdCount = size
+    , ifdOffset = 0
+    , ifdExtended = d
+    }
+  where
+    size = fromIntegral $ F.length v
+    at i = fromIntegral $ v V.! i
+    combined = (at 0  `unsafeShiftL` 16) .|. at 1
+makeIfd t d@(ExifLongs v)
+  | size == 1 = ImageFileDirectory
+    { ifdIdentifier = t
+    , ifdType = TypeLong
+    , ifdCount = 1
+    , ifdOffset = v V.! 0
+    , ifdExtended = ExifNone
+    }
+  | otherwise = ImageFileDirectory
+    { ifdIdentifier = t
+    , ifdType = TypeLong
+    , ifdCount = size
+    , ifdOffset = 0
+    , ifdExtended = d
+    }
+  where size = fromIntegral $ F.length v
+makeIfd t s@(ExifString str) = ImageFileDirectory
+    { ifdIdentifier = t
+    , ifdType = TypeAscii
+    , ifdCount = fromIntegral $ BC.length str
+    , ifdOffset = 0
+    , ifdExtended = s
+    }
+makeIfd t s@(ExifUndefined str)
+  | size > 4 = ImageFileDirectory
+    { ifdIdentifier = t
+    , ifdType = TypeUndefined
+    , ifdCount = size
+    , ifdOffset = 0
+    , ifdExtended = s
+    }
+  | otherwise = ImageFileDirectory
+    { ifdIdentifier = t
+    , ifdType = TypeUndefined
+    , ifdCount = size
+    , ifdOffset = ofs
+    , ifdExtended = ExifNone
+    }
+  where
+    size = fromIntegral $ BC.length str
+    at ix
+      | fromIntegral ix < size = fromIntegral $ B.index str ix `unsafeShiftL` (4 - (8 * ix))
+      | otherwise = 0
+    ofs = at 0 .|. at 1 .|. at 2 .|. at 3
+makeIfd t d = ImageFileDirectory
+  { ifdIdentifier = t
+  , ifdType = typeOfData d
+  , ifdCount = 1
+  , ifdOffset = 0
+  , ifdExtended = d
+  }
+
+encodeTiffStringMetadata :: Metadatas -> [ImageFileDirectory]
+encodeTiffStringMetadata metas = sortBy (compare `on` word16OfTag . ifdIdentifier) $ allTags where
+  keyStr tag k = case Met.lookup k metas of
+    Nothing -> mempty
+    Just v -> pure . makeIfd tag . ExifString $ BC.pack v
+  allTags = copyright <> artist <> title <> description <> software <> allPureExif
+
+  allPureExif = fmap (uncurry makeIfd) $ extractExifMetas metas
+
+  copyright = keyStr TagCopyright Met.Copyright
+  artist = keyStr TagArtist Met.Author
+  title = keyStr TagDocumentName Met.Title
+  description = keyStr TagImageDescription Met.Description
+  software = keyStr TagSoftware Met.Software
+
+extractTiffStringMetadata :: [ImageFileDirectory] -> Metadatas
+extractTiffStringMetadata = Met.insert Met.Format Met.SourceTiff . foldMap go where
+  strMeta k = Met.singleton k . BC.unpack
+  exif ifd =
+    Met.singleton (Met.Exif $ ifdIdentifier ifd) $ ifdExtended ifd
+  inserter acc (k, v) = Met.insert (Met.Exif k) v acc
+  exifShort ifd =
+    Met.singleton (Met.Exif $ ifdIdentifier ifd) . (ExifShort . fromIntegral) $ ifdOffset ifd
+
+  go :: ImageFileDirectory -> Metadatas
+  go ifd = case (ifdIdentifier ifd, ifdExtended ifd) of
+    (TagArtist, ExifString v) -> strMeta Met.Author v
+    (TagBitsPerSample, _) -> mempty
+    (TagColorMap, _) -> mempty
+    (TagCompression, _) -> mempty
+    (TagCopyright, ExifString v) -> strMeta Met.Copyright v
+    (TagDocumentName, ExifString v) -> strMeta Met.Title v
+    (TagExifOffset, ExifIFD lst) -> F.foldl' inserter mempty lst
+    (TagImageDescription, ExifString v) -> strMeta Met.Description v
+    (TagImageLength, _) -> Met.singleton Met.Height . fromIntegral $ ifdOffset ifd
+    (TagImageWidth, _) -> Met.singleton Met.Width . fromIntegral $ ifdOffset ifd
+    (TagJPEGACTables, _) -> mempty
+    (TagJPEGDCTables, _) -> mempty
+    (TagJPEGInterchangeFormat, _) -> mempty
+    (TagJPEGInterchangeFormatLength, _) -> mempty
+    (TagJPEGLosslessPredictors, _) -> mempty
+    (TagJPEGPointTransforms, _) -> mempty
+    (TagJPEGQTables, _) -> mempty
+    (TagJPEGRestartInterval, _) -> mempty
+    (TagJpegProc, _) -> mempty
+    (TagModel, v) -> Met.singleton (Met.Exif TagModel) v
+    (TagMake, v) -> Met.singleton (Met.Exif TagMake) v
+    (TagOrientation, _) -> exifShort ifd
+    (TagResolutionUnit, _) -> mempty
+    (TagRowPerStrip, _) -> mempty
+    (TagSamplesPerPixel, _) -> mempty
+    (TagSoftware, ExifString v) -> strMeta Met.Software v
+    (TagStripByteCounts, _) -> mempty
+    (TagStripOffsets, _) -> mempty
+    (TagTileByteCount, _) -> mempty
+    (TagTileLength, _) -> mempty
+    (TagTileOffset, _) -> mempty
+    (TagTileWidth, _) -> mempty
+    (TagUnknown _, _) -> exif ifd
+    (TagXResolution, _) -> mempty
+    (TagYCbCrCoeff, _) -> mempty
+    (TagYCbCrPositioning, _) -> mempty
+    (TagYCbCrSubsampling, _) -> mempty
+    (TagYResolution, _) -> mempty
+    _ -> mempty
+
+byTag :: ExifTag -> ImageFileDirectory -> Bool
+byTag t ifd = ifdIdentifier ifd == t
+
+data TiffResolutionUnit
+  = ResolutionUnitUnknown
+  | ResolutionUnitInch
+  | ResolutionUnitCentimeter
+
+unitOfIfd :: ImageFileDirectory -> TiffResolutionUnit
+unitOfIfd ifd = case (ifdType ifd, ifdOffset ifd) of
+  (TypeShort, 1) -> ResolutionUnitUnknown
+  (TypeShort, 2) -> ResolutionUnitInch
+  (TypeShort, 3) -> ResolutionUnitCentimeter
+  _ -> ResolutionUnitUnknown
+
+extractTiffDpiMetadata :: [ImageFileDirectory] -> Metadatas
+extractTiffDpiMetadata lst = go where
+  go = case unitOfIfd <$> find (byTag TagResolutionUnit) lst of
+    Nothing -> mempty
+    Just ResolutionUnitUnknown -> mempty
+    Just ResolutionUnitCentimeter -> findDpis Met.dotsPerCentiMeterToDotPerInch mempty
+    Just ResolutionUnitInch -> findDpis id mempty
+
+  findDpis toDpi =
+     findDpi Met.DpiX TagXResolution toDpi . findDpi Met.DpiY TagYResolution toDpi
+
+  findDpi k tag toDpi metas = case find (byTag tag) lst of
+    Nothing -> metas
+    Just ImageFileDirectory { ifdExtended = ExifRational num den } ->
+      Met.insert k (toDpi . fromIntegral $ num `div` den) metas
+    Just _ -> metas
+
+extractTiffMetadata :: [ImageFileDirectory] -> Metadatas
+extractTiffMetadata lst = extractTiffDpiMetadata lst <> extractTiffStringMetadata lst
+
+ src/Codec/Picture/Tiff/Internal/Types.hs view
@@ -0,0 +1,490 @@+{-# LANGUAGE CPP #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE FlexibleContexts #-}
+module Codec.Picture.Tiff.Internal.Types
+    ( BinaryParam( .. )
+    , Endianness( .. )
+    , TiffHeader( .. )
+    , TiffPlanarConfiguration( .. )
+    , TiffCompression( .. )
+    , IfdType( .. )
+    , TiffColorspace( .. )
+    , TiffSampleFormat( .. )
+    , ImageFileDirectory( .. )
+    , ExtraSample( .. )
+    , Predictor( .. )
+
+    , planarConfgOfConstant
+    , constantToPlaneConfiguration
+    , unpackSampleFormat
+    , packSampleFormat
+    , word16OfTag
+    , unpackPhotometricInterpretation
+    , packPhotometricInterpretation
+    , codeOfExtraSample
+    , unPackCompression
+    , packCompression 
+    , predictorOfConstant
+    ) where
+
+#if !MIN_VERSION_base(4,8,0)
+import Control.Applicative( (<$>), (<*>), pure )
+#endif
+
+import Control.Monad( forM_, when, replicateM, )
+import Data.Bits( (.&.), unsafeShiftR )
+import Data.Binary( Binary( .. ) )
+import Data.Binary.Get( Get
+                      , getWord16le, getWord16be
+                      , getWord32le, getWord32be
+                      , bytesRead
+                      , skip
+                      , getByteString
+                      )
+import Data.Binary.Put( Put
+                      , putWord16le, putWord16be
+                      , putWord32le, putWord32be
+                      , putByteString
+                      )
+import Data.Function( on )
+import Data.List( sortBy, mapAccumL )
+import qualified Data.Vector as V
+import qualified Data.ByteString as B
+import Data.Int( Int32 )
+import Data.Word( Word8, Word16, Word32 )
+
+import Codec.Picture.Metadata.Exif
+{-import Debug.Trace-}
+
+data Endianness
+  = EndianLittle
+  | EndianBig
+  deriving (Eq, Show)
+
+instance Binary Endianness where
+  put EndianLittle = putWord16le 0x4949
+  put EndianBig = putWord16le 0x4D4D
+
+  get = do
+    tag <- getWord16le
+    case tag of
+      0x4949 -> return EndianLittle
+      0x4D4D -> return EndianBig
+      _ -> fail "Invalid endian tag value"
+
+-- | Because having a polymorphic get with endianness is to nice
+-- to pass on, introducing this helper type class, which is just
+-- a superset of Binary, but formalising a parameter passing
+-- into it.
+class BinaryParam a b where
+  getP :: a -> Get b
+  putP :: a -> b -> Put
+
+data TiffHeader = TiffHeader
+  { hdrEndianness :: !Endianness
+  , hdrOffset     :: {-# UNPACK #-} !Word32
+  }
+  deriving (Eq, Show)
+
+instance BinaryParam Endianness Word16 where
+  putP EndianLittle = putWord16le
+  putP EndianBig = putWord16be
+
+  getP EndianLittle = getWord16le
+  getP EndianBig = getWord16be
+
+instance BinaryParam Endianness Int32 where
+  putP en v = putP en $ (fromIntegral v :: Word32)
+  getP en = fromIntegral <$> (getP en :: Get Word32) 
+
+instance BinaryParam Endianness Word32 where
+  putP EndianLittle = putWord32le
+  putP EndianBig = putWord32be
+
+  getP EndianLittle = getWord32le
+  getP EndianBig = getWord32be
+
+instance Binary TiffHeader where
+  put hdr = do
+    let endian = hdrEndianness hdr
+    put endian
+    putP endian (42 :: Word16)
+    putP endian $ hdrOffset hdr
+
+  get = do
+    endian <- get
+    magic <- getP endian
+    let magicValue = 42 :: Word16
+    when (magic /= magicValue)
+         (fail "Invalid TIFF magic number")
+    TiffHeader endian <$> getP endian
+
+data TiffPlanarConfiguration
+  = PlanarConfigContig    -- = 1
+  | PlanarConfigSeparate  -- = 2
+
+planarConfgOfConstant :: Word32 -> Get TiffPlanarConfiguration
+planarConfgOfConstant 0 = pure PlanarConfigContig
+planarConfgOfConstant 1 = pure PlanarConfigContig
+planarConfgOfConstant 2 = pure PlanarConfigSeparate
+planarConfgOfConstant v = fail $ "Unknown planar constant (" ++ show v ++ ")"
+
+constantToPlaneConfiguration :: TiffPlanarConfiguration -> Word16
+constantToPlaneConfiguration PlanarConfigContig = 1
+constantToPlaneConfiguration PlanarConfigSeparate = 2
+
+data TiffCompression
+  = CompressionNone           -- 1
+  | CompressionModifiedRLE    -- 2
+  | CompressionLZW            -- 5
+  | CompressionJPEG           -- 6
+  | CompressionPackBit        -- 32273
+
+data IfdType
+  = TypeByte
+  | TypeAscii
+  | TypeShort
+  | TypeLong
+  | TypeRational
+  | TypeSByte
+  | TypeUndefined
+  | TypeSignedShort
+  | TypeSignedLong
+  | TypeSignedRational
+  | TypeFloat
+  | TypeDouble
+  deriving Show
+
+instance BinaryParam Endianness IfdType where
+    getP endianness = getP endianness >>= conv where
+      conv :: Word16 -> Get IfdType
+      conv v = case v of
+        1  -> return TypeByte
+        2  -> return TypeAscii
+        3  -> return TypeShort
+        4  -> return TypeLong
+        5  -> return TypeRational
+        6  -> return TypeSByte
+        7  -> return TypeUndefined
+        8  -> return TypeSignedShort
+        9  -> return TypeSignedLong
+        10 -> return TypeSignedRational
+        11 -> return TypeFloat
+        12 -> return TypeDouble
+        _  -> fail "Invalid TIF directory type"
+
+    putP endianness = putP endianness . conv where
+      conv :: IfdType -> Word16
+      conv v = case v of
+        TypeByte -> 1
+        TypeAscii -> 2
+        TypeShort -> 3
+        TypeLong -> 4
+        TypeRational -> 5
+        TypeSByte -> 6
+        TypeUndefined -> 7
+        TypeSignedShort -> 8
+        TypeSignedLong -> 9
+        TypeSignedRational -> 10
+        TypeFloat -> 11
+        TypeDouble -> 12
+
+instance BinaryParam Endianness ExifTag where
+  getP endianness = tagOfWord16 <$> getP endianness
+  putP endianness = putP endianness . word16OfTag
+
+data Predictor
+  = PredictorNone                   -- 1
+  | PredictorHorizontalDifferencing -- 2
+  deriving Eq
+
+predictorOfConstant :: Word32 -> Get Predictor
+predictorOfConstant 1 = pure PredictorNone
+predictorOfConstant 2 = pure PredictorHorizontalDifferencing
+predictorOfConstant v = fail $ "Unknown predictor (" ++ show v ++ ")"
+
+paddWrite :: B.ByteString -> Put
+paddWrite str = putByteString str >> padding where
+  zero = 0 :: Word8
+  padding = when (odd (B.length str)) $ put zero
+
+instance BinaryParam (Endianness, Int, ImageFileDirectory) ExifData where
+  putP (endianness, _, _) = dump
+    where
+      dump ExifNone = pure ()
+      dump (ExifLong _) = pure ()
+      dump (ExifShort _) = pure ()
+      dump (ExifIFD _) = pure ()
+      dump (ExifString bstr) = paddWrite bstr
+      dump (ExifUndefined bstr) = paddWrite bstr
+      -- wrong if length == 2
+      dump (ExifShorts shorts) = V.mapM_ (putP endianness) shorts
+      dump (ExifLongs longs) = V.mapM_ (putP endianness) longs
+      dump (ExifRational a b) = putP endianness a >> putP endianness b
+      dump (ExifSignedRational a b) = putP endianness a >> putP endianness b
+
+  getP (endianness, maxi, ifd) = fetcher ifd
+    where
+      align ImageFileDirectory { ifdOffset = offset } act = do
+        readed <- bytesRead
+        let delta = fromIntegral offset - readed
+        if offset >= fromIntegral maxi || fromIntegral readed > offset then
+          pure ExifNone
+        else do
+          skip $ fromIntegral delta
+          act
+
+      getE :: (BinaryParam Endianness a) => Get a
+      getE = getP endianness
+
+      getVec count = V.replicateM (fromIntegral count)
+
+      fetcher ImageFileDirectory { ifdIdentifier = TagExifOffset
+                                 , ifdType = TypeLong
+                                 , ifdCount = 1 } = do
+         align ifd $ do
+            let byOffset = sortBy (compare `on` ifdOffset)
+                cleansIfds = fmap (cleanImageFileDirectory endianness)
+            subIfds <- cleansIfds . byOffset <$> getP endianness
+            cleaned <- fetchExtended endianness maxi $ sortBy (compare `on` ifdOffset) subIfds
+            pure $ ExifIFD [(ifdIdentifier fd, ifdExtended fd) | fd <- cleaned]
+         {-  
+      fetcher ImageFileDirectory { ifdIdentifier = TagGPSInfo
+                                 , ifdType = TypeLong
+                                 , ifdCount = 1 } = do
+         align ifd 
+         subIfds <- fmap (cleanImageFileDirectory endianness) <$> getP endianness
+         cleaned <- fetchExtended endianness subIfds
+         pure $ ExifIFD [(ifdIdentifier fd, ifdExtended fd) | fd <- cleaned]
+        -}
+      fetcher ImageFileDirectory { ifdType = TypeUndefined, ifdCount = count } | count > 4 =
+         align ifd $ ExifUndefined <$> getByteString (fromIntegral count)
+      fetcher ImageFileDirectory { ifdType = TypeUndefined, ifdOffset = ofs } =
+          pure . ExifUndefined . B.pack $ take (fromIntegral $ ifdCount ifd)
+              [fromIntegral $ ofs .&. 0xFF000000 `unsafeShiftR` (3 * 8)
+              ,fromIntegral $ ofs .&. 0x00FF0000 `unsafeShiftR` (2 * 8)
+              ,fromIntegral $ ofs .&. 0x0000FF00 `unsafeShiftR` (1 * 8)
+              ,fromIntegral $ ofs .&. 0x000000FF
+              ]
+      fetcher ImageFileDirectory { ifdType = TypeAscii, ifdCount = count } | count > 1 =
+          align ifd $ ExifString <$> getByteString (fromIntegral count)
+      fetcher ImageFileDirectory { ifdType = TypeShort, ifdCount = 2, ifdOffset = ofs } =
+          pure . ExifShorts $ V.fromListN 2 valList
+            where high = fromIntegral $ ofs `unsafeShiftR` 16
+                  low = fromIntegral $ ofs .&. 0xFFFF
+                  valList = case endianness of
+                    EndianLittle -> [low, high]
+                    EndianBig -> [high, low]
+      fetcher ImageFileDirectory { ifdType = TypeRational, ifdCount = 1 } = do
+          align ifd $ ExifRational <$> getP EndianLittle <*> getP EndianLittle
+      fetcher ImageFileDirectory { ifdType = TypeSignedRational, ifdCount = 1 } = do
+          align ifd $ ExifSignedRational <$> getP EndianLittle <*> getP EndianLittle
+      fetcher ImageFileDirectory { ifdType = TypeShort, ifdCount = 1 } =
+          pure . ExifShort . fromIntegral $ ifdOffset ifd
+      fetcher ImageFileDirectory { ifdType = TypeShort, ifdCount = count } | count > 2 =
+          align ifd $ ExifShorts <$> getVec count getE
+      fetcher ImageFileDirectory { ifdType = TypeLong, ifdCount = 1 } =
+          pure . ExifLong . fromIntegral $ ifdOffset ifd
+      fetcher ImageFileDirectory { ifdType = TypeLong, ifdCount = count } | count > 1 =
+          align ifd $ ExifLongs <$> getVec count getE
+      fetcher _ = pure ExifNone
+
+cleanImageFileDirectory :: Endianness -> ImageFileDirectory -> ImageFileDirectory
+cleanImageFileDirectory EndianBig ifd@(ImageFileDirectory { ifdCount = 1 }) = aux $ ifdType ifd
+  where
+    aux TypeShort = ifd { ifdOffset = ifdOffset ifd `unsafeShiftR` 16 }
+    aux _ = ifd
+cleanImageFileDirectory _ ifd = ifd
+
+fetchExtended :: Endianness -> Int -> [ImageFileDirectory] -> Get [ImageFileDirectory]
+fetchExtended endian maxi = mapM $ \ifd -> do
+  v <- getP (endian, maxi, ifd)
+  pure $ ifd { ifdExtended = v }
+
+-- | All the IFD must be written in order according to the tag
+-- value of the IFD. To avoid getting to much restriction in the
+-- serialization code, just sort it.
+orderIfdByTag :: [ImageFileDirectory] -> [ImageFileDirectory]
+orderIfdByTag = sortBy comparer where
+  comparer a b = compare t1 t2 where
+    t1 = word16OfTag $ ifdIdentifier a
+    t2 = word16OfTag $ ifdIdentifier b
+
+-- | Given an official offset and a list of IFD, update the offset information
+-- of the IFD with extended data.
+setupIfdOffsets :: Word32 -> [ImageFileDirectory] -> (Word32, [ImageFileDirectory])
+setupIfdOffsets initialOffset lst = mapAccumL updater startExtended lst
+  where ifdElementCount = fromIntegral $ length lst
+        ifdSize = 12
+        ifdCountSize = 2
+        nextOffsetSize = 4
+        startExtended = initialOffset
+                     + ifdElementCount * ifdSize
+                     + ifdCountSize + nextOffsetSize
+
+        paddedSize blob = fromIntegral $ blobLength + padding where
+          blobLength = B.length blob
+          padding = if odd blobLength then 1 else 0
+
+        updater ix ifd@(ImageFileDirectory { ifdIdentifier = TagExifOffset }) =
+            (ix, ifd { ifdOffset = ix } )
+        updater ix ifd@(ImageFileDirectory { ifdExtended = ExifUndefined b }) =
+            (ix + paddedSize b, ifd { ifdOffset = ix } )
+        updater ix ifd@(ImageFileDirectory { ifdExtended = ExifString b }) =
+            (ix + paddedSize b, ifd { ifdOffset = ix } )
+        updater ix ifd@(ImageFileDirectory { ifdExtended = ExifLongs v })
+            | V.length v > 1 = ( ix + fromIntegral (V.length v * 4)
+                               , ifd { ifdOffset = ix } )
+        updater ix ifd@(ImageFileDirectory { ifdExtended = ExifShorts v })
+            | V.length v > 2 = ( ix + fromIntegral (V.length v * 2)
+                             , ifd { ifdOffset = ix })
+        updater ix ifd = (ix, ifd)
+
+instance BinaryParam B.ByteString (TiffHeader, [[ImageFileDirectory]]) where
+  putP rawData (hdr, ifds) = do
+    put hdr
+    putByteString rawData
+    let endianness = hdrEndianness hdr
+        (_, offseted) = mapAccumL
+            (\ix ifd -> setupIfdOffsets ix $ orderIfdByTag ifd)
+            (hdrOffset hdr)
+            ifds
+    forM_ offseted $ \list -> do
+        putP endianness list
+        mapM_ (\field -> putP (endianness, (0::Int), field) $ ifdExtended field) list
+
+  getP raw = do
+    hdr <- get
+    readed <- bytesRead
+    skip . fromIntegral $ fromIntegral (hdrOffset hdr) - readed
+    let endian = hdrEndianness hdr
+        byOffset = sortBy (compare `on` ifdOffset)
+        cleanIfds = fmap (cleanImageFileDirectory endian)
+
+    ifd <-  cleanIfds . byOffset <$> getP endian
+    cleaned <- fetchExtended endian (B.length raw) ifd
+    return (hdr, [cleaned])
+
+data TiffSampleFormat
+  = TiffSampleUint
+  | TiffSampleInt
+  | TiffSampleFloat
+  | TiffSampleUnknown
+  deriving Eq
+
+unpackSampleFormat :: Word32 -> Get TiffSampleFormat
+unpackSampleFormat v = case v of
+  1 -> pure TiffSampleUint
+  2 -> pure TiffSampleInt
+  3 -> pure TiffSampleFloat
+  4 -> pure TiffSampleUnknown
+  vv -> fail $ "Undefined data format (" ++ show vv ++ ")"
+
+packSampleFormat :: TiffSampleFormat -> Word32
+packSampleFormat TiffSampleUint    = 1
+packSampleFormat TiffSampleInt     = 2
+packSampleFormat TiffSampleFloat   = 3
+packSampleFormat TiffSampleUnknown = 4
+
+data ImageFileDirectory = ImageFileDirectory
+  { ifdIdentifier :: !ExifTag -- Word16
+  , ifdType       :: !IfdType -- Word16
+  , ifdCount      :: !Word32
+  , ifdOffset     :: !Word32
+  , ifdExtended   :: !ExifData
+  }
+  deriving Show
+
+instance BinaryParam Endianness ImageFileDirectory where
+  getP endianness =
+    ImageFileDirectory <$> getE <*> getE <*> getE <*> getE
+                       <*> pure ExifNone
+        where getE :: (BinaryParam Endianness a) => Get a
+              getE = getP endianness
+
+  putP endianness ifd = do
+    let putE :: (BinaryParam Endianness a) => a -> Put
+        putE = putP endianness
+    putE $ ifdIdentifier ifd
+    putE $ ifdType ifd
+    putE $ ifdCount ifd
+    putE $ ifdOffset ifd
+
+instance BinaryParam Endianness [ImageFileDirectory] where
+  getP endianness = do
+    count <- getP endianness :: Get Word16
+    rez <- replicateM (fromIntegral count) $ getP endianness
+    _ <- getP endianness :: Get Word32
+    pure rez
+
+
+  putP endianness lst = do
+    let count = fromIntegral $ length lst :: Word16
+    putP endianness count
+    mapM_ (putP endianness) lst
+    putP endianness (0 :: Word32)
+
+data TiffColorspace
+  = TiffMonochromeWhite0 -- ^ 0
+  | TiffMonochrome       -- ^ 1
+  | TiffRGB              -- ^ 2
+  | TiffPaleted          -- ^ 3
+  | TiffTransparencyMask -- ^ 4
+  | TiffCMYK             -- ^ 5
+  | TiffYCbCr            -- ^ 6
+  | TiffCIELab           -- ^ 8
+
+
+packPhotometricInterpretation :: TiffColorspace -> Word16
+packPhotometricInterpretation v = case v of
+  TiffMonochromeWhite0 -> 0
+  TiffMonochrome       -> 1
+  TiffRGB              -> 2
+  TiffPaleted          -> 3
+  TiffTransparencyMask -> 4
+  TiffCMYK             -> 5
+  TiffYCbCr            -> 6
+  TiffCIELab           -> 8
+
+unpackPhotometricInterpretation :: Word32 -> Get TiffColorspace
+unpackPhotometricInterpretation v = case v of
+  0 -> pure TiffMonochromeWhite0
+  1 -> pure TiffMonochrome
+  2 -> pure TiffRGB
+  3 -> pure TiffPaleted
+  4 -> pure TiffTransparencyMask
+  5 -> pure TiffCMYK
+  6 -> pure TiffYCbCr
+  8 -> pure TiffCIELab
+  vv -> fail $ "Unrecognized color space " ++ show vv
+
+data ExtraSample
+  = ExtraSampleUnspecified       -- ^ 0
+  | ExtraSampleAssociatedAlpha   -- ^ 1
+  | ExtraSampleUnassociatedAlpha -- ^ 2
+
+codeOfExtraSample :: ExtraSample -> Word16
+codeOfExtraSample v = case v of
+  ExtraSampleUnspecified -> 0
+  ExtraSampleAssociatedAlpha -> 1
+  ExtraSampleUnassociatedAlpha -> 2
+
+unPackCompression :: Word32 -> Get TiffCompression
+unPackCompression v = case v of
+  0 -> pure CompressionNone
+  1 -> pure CompressionNone
+  2 -> pure CompressionModifiedRLE
+  5 -> pure CompressionLZW
+  6 -> pure CompressionJPEG
+  32773 -> pure CompressionPackBit
+  vv -> fail $ "Unknown compression scheme " ++ show vv
+
+packCompression :: TiffCompression -> Word16
+packCompression v = case v of
+  CompressionNone        -> 1
+  CompressionModifiedRLE -> 2
+  CompressionLZW         -> 5
+  CompressionJPEG        -> 6
+  CompressionPackBit     -> 32773
+
− src/Codec/Picture/Tiff/Metadata.hs
@@ -1,237 +0,0 @@-{-# LANGUAGE CPP #-}
-module Codec.Picture.Tiff.Metadata
-    ( extractTiffMetadata
-    , encodeTiffStringMetadata
-    , exifOffsetIfd
-    ) where
-
-#if !MIN_VERSION_base(4,8,0)
-import Data.Monoid( mempty )
-import Data.Foldable( foldMap )
-import Control.Applicative( (<$>) )
-#endif
-
-import Data.Bits( unsafeShiftL, (.|.) )
-import Data.Foldable( find )
-import Data.List( sortBy )
-import Data.Function( on )
-import qualified Data.Foldable as F
-import Data.Monoid( (<>) )
-import Codec.Picture.Metadata( Metadatas )
-import qualified Data.ByteString as B
-import qualified Data.ByteString.Char8 as BC
-import qualified Codec.Picture.Metadata as Met
-import qualified Data.Vector.Generic as V
-import Codec.Picture.Tiff.Types
-import Codec.Picture.Metadata( extractExifMetas )
-import Codec.Picture.Metadata.Exif
-
-exifOffsetIfd :: ImageFileDirectory
-exifOffsetIfd = ImageFileDirectory
-  { ifdIdentifier = TagExifOffset
-  , ifdCount = 1
-  , ifdType = TypeLong
-  , ifdOffset = 0
-  , ifdExtended = ExifNone
-  }
-
-typeOfData :: ExifData -> IfdType
-typeOfData d = case d of
-  ExifNone -> error "Impossible - typeOfData : ExifNone"
-  ExifIFD _exifs -> error "Impossible - typeOfData : ExifIFD"
-  ExifLong _l -> TypeLong
-  ExifLongs _l -> TypeLong
-  ExifShort _s -> TypeShort
-  ExifShorts _s -> TypeShort
-  ExifString _str -> TypeAscii
-  ExifUndefined _undef -> TypeUndefined
-  ExifRational _r1 _r2 -> TypeRational
-  ExifSignedRational _sr1 _sr2 -> TypeSignedRational
-
-makeIfd :: ExifTag -> ExifData -> ImageFileDirectory
-makeIfd t (ExifShort v) = ImageFileDirectory
-  { ifdIdentifier = t
-  , ifdType = TypeShort
-  , ifdCount = 1
-  , ifdOffset = fromIntegral v `unsafeShiftL` 16
-  , ifdExtended = ExifNone
-  }
-makeIfd t (ExifLong v) = ImageFileDirectory 
-  { ifdIdentifier = t
-  , ifdType = TypeLong
-  , ifdCount = 1
-  , ifdOffset = fromIntegral v
-  , ifdExtended = ExifNone
-  }
-makeIfd t d@(ExifShorts v)
-  | size == 2 = ImageFileDirectory
-    { ifdIdentifier = t
-    , ifdType = TypeShort
-    , ifdCount = 2
-    , ifdOffset = combined
-    , ifdExtended = ExifNone
-    }
-  | otherwise = ImageFileDirectory
-    { ifdIdentifier = t
-    , ifdType = TypeShort
-    , ifdCount = size
-    , ifdOffset = 0
-    , ifdExtended = d
-    }
-  where
-    size = fromIntegral $ F.length v
-    at i = fromIntegral $ v V.! i
-    combined = (at 0  `unsafeShiftL` 16) .|. at 1
-makeIfd t d@(ExifLongs v)
-  | size == 1 = ImageFileDirectory
-    { ifdIdentifier = t
-    , ifdType = TypeLong
-    , ifdCount = 1
-    , ifdOffset = v V.! 0
-    , ifdExtended = ExifNone
-    }
-  | otherwise = ImageFileDirectory
-    { ifdIdentifier = t
-    , ifdType = TypeLong
-    , ifdCount = size
-    , ifdOffset = 0
-    , ifdExtended = d
-    }
-  where size = fromIntegral $ F.length v
-makeIfd t s@(ExifString str) = ImageFileDirectory
-    { ifdIdentifier = t
-    , ifdType = TypeAscii
-    , ifdCount = fromIntegral $ BC.length str
-    , ifdOffset = 0
-    , ifdExtended = s
-    }
-makeIfd t s@(ExifUndefined str)
-  | size > 4 = ImageFileDirectory
-    { ifdIdentifier = t
-    , ifdType = TypeUndefined
-    , ifdCount = size
-    , ifdOffset = 0
-    , ifdExtended = s
-    }
-  | otherwise = ImageFileDirectory
-    { ifdIdentifier = t
-    , ifdType = TypeUndefined
-    , ifdCount = size
-    , ifdOffset = ofs
-    , ifdExtended = ExifNone
-    }
-  where
-    size = fromIntegral $ BC.length str
-    at ix
-      | fromIntegral ix < size = fromIntegral $ B.index str ix `unsafeShiftL` (4 - (8 * ix))
-      | otherwise = 0
-    ofs = at 0 .|. at 1 .|. at 2 .|. at 3
-makeIfd t d = ImageFileDirectory
-  { ifdIdentifier = t
-  , ifdType = typeOfData d
-  , ifdCount = 1
-  , ifdOffset = 0
-  , ifdExtended = d
-  }
-
-encodeTiffStringMetadata :: Metadatas -> [ImageFileDirectory]
-encodeTiffStringMetadata metas = sortBy (compare `on` word16OfTag . ifdIdentifier) $ allTags where
-  keyStr tag k = case Met.lookup k metas of
-    Nothing -> mempty
-    Just v -> pure . makeIfd tag . ExifString $ BC.pack v
-  allTags = copyright <> artist <> title <> description <> software <> allPureExif
-
-  allPureExif = fmap (uncurry makeIfd) $ extractExifMetas metas
-
-  copyright = keyStr TagCopyright Met.Copyright
-  artist = keyStr TagArtist Met.Author
-  title = keyStr TagDocumentName Met.Title
-  description = keyStr TagImageDescription Met.Description
-  software = keyStr TagSoftware Met.Software
-
-extractTiffStringMetadata :: [ImageFileDirectory] -> Metadatas
-extractTiffStringMetadata = Met.insert Met.Format Met.SourceTiff . foldMap go where
-  strMeta k = Met.singleton k . BC.unpack
-  exif ifd =
-    Met.singleton (Met.Exif $ ifdIdentifier ifd) $ ifdExtended ifd
-  inserter acc (k, v) = Met.insert (Met.Exif k) v acc
-  exifShort ifd =
-    Met.singleton (Met.Exif $ ifdIdentifier ifd) . (ExifShort . fromIntegral) $ ifdOffset ifd
-
-  go :: ImageFileDirectory -> Metadatas
-  go ifd = case (ifdIdentifier ifd, ifdExtended ifd) of
-    (TagArtist, ExifString v) -> strMeta Met.Author v
-    (TagBitsPerSample, _) -> mempty
-    (TagColorMap, _) -> mempty
-    (TagCompression, _) -> mempty
-    (TagCopyright, ExifString v) -> strMeta Met.Copyright v
-    (TagDocumentName, ExifString v) -> strMeta Met.Title v
-    (TagExifOffset, ExifIFD lst) -> F.foldl' inserter mempty lst
-    (TagImageDescription, ExifString v) -> strMeta Met.Description v
-    (TagImageLength, _) -> Met.singleton Met.Height . fromIntegral $ ifdOffset ifd
-    (TagImageWidth, _) -> Met.singleton Met.Width . fromIntegral $ ifdOffset ifd
-    (TagJPEGACTables, _) -> mempty
-    (TagJPEGDCTables, _) -> mempty
-    (TagJPEGInterchangeFormat, _) -> mempty
-    (TagJPEGInterchangeFormatLength, _) -> mempty
-    (TagJPEGLosslessPredictors, _) -> mempty
-    (TagJPEGPointTransforms, _) -> mempty
-    (TagJPEGQTables, _) -> mempty
-    (TagJPEGRestartInterval, _) -> mempty
-    (TagJpegProc, _) -> mempty
-    (TagModel, v) -> Met.singleton (Met.Exif TagModel) v
-    (TagMake, v) -> Met.singleton (Met.Exif TagMake) v
-    (TagOrientation, _) -> exifShort ifd
-    (TagResolutionUnit, _) -> mempty
-    (TagRowPerStrip, _) -> mempty
-    (TagSamplesPerPixel, _) -> mempty
-    (TagSoftware, ExifString v) -> strMeta Met.Software v
-    (TagStripByteCounts, _) -> mempty
-    (TagStripOffsets, _) -> mempty
-    (TagTileByteCount, _) -> mempty
-    (TagTileLength, _) -> mempty
-    (TagTileOffset, _) -> mempty
-    (TagTileWidth, _) -> mempty
-    (TagUnknown _, _) -> exif ifd
-    (TagXResolution, _) -> mempty
-    (TagYCbCrCoeff, _) -> mempty
-    (TagYCbCrPositioning, _) -> mempty
-    (TagYCbCrSubsampling, _) -> mempty
-    (TagYResolution, _) -> mempty
-    _ -> mempty
-
-byTag :: ExifTag -> ImageFileDirectory -> Bool
-byTag t ifd = ifdIdentifier ifd == t
-
-data TiffResolutionUnit
-  = ResolutionUnitUnknown
-  | ResolutionUnitInch
-  | ResolutionUnitCentimeter
-
-unitOfIfd :: ImageFileDirectory -> TiffResolutionUnit
-unitOfIfd ifd = case (ifdType ifd, ifdOffset ifd) of
-  (TypeShort, 1) -> ResolutionUnitUnknown
-  (TypeShort, 2) -> ResolutionUnitInch
-  (TypeShort, 3) -> ResolutionUnitCentimeter
-  _ -> ResolutionUnitUnknown
-
-extractTiffDpiMetadata :: [ImageFileDirectory] -> Metadatas
-extractTiffDpiMetadata lst = go where
-  go = case unitOfIfd <$> find (byTag TagResolutionUnit) lst of
-    Nothing -> mempty
-    Just ResolutionUnitUnknown -> mempty
-    Just ResolutionUnitCentimeter -> findDpis Met.dotsPerCentiMeterToDotPerInch mempty
-    Just ResolutionUnitInch -> findDpis id mempty
-
-  findDpis toDpi =
-     findDpi Met.DpiX TagXResolution toDpi . findDpi Met.DpiY TagYResolution toDpi
-
-  findDpi k tag toDpi metas = case find (byTag tag) lst of
-    Nothing -> metas
-    Just ImageFileDirectory { ifdExtended = ExifRational num den } ->
-      Met.insert k (toDpi . fromIntegral $ num `div` den) metas
-    Just _ -> metas
-
-extractTiffMetadata :: [ImageFileDirectory] -> Metadatas
-extractTiffMetadata lst = extractTiffDpiMetadata lst <> extractTiffStringMetadata lst
-
− src/Codec/Picture/Tiff/Types.hs
@@ -1,490 +0,0 @@-{-# LANGUAGE CPP #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE FlexibleContexts #-}
-module Codec.Picture.Tiff.Types
-    ( BinaryParam( .. )
-    , Endianness( .. )
-    , TiffHeader( .. )
-    , TiffPlanarConfiguration( .. )
-    , TiffCompression( .. )
-    , IfdType( .. )
-    , TiffColorspace( .. )
-    , TiffSampleFormat( .. )
-    , ImageFileDirectory( .. )
-    , ExtraSample( .. )
-    , Predictor( .. )
-
-    , planarConfgOfConstant
-    , constantToPlaneConfiguration
-    , unpackSampleFormat
-    , packSampleFormat
-    , word16OfTag
-    , unpackPhotometricInterpretation
-    , packPhotometricInterpretation
-    , codeOfExtraSample
-    , unPackCompression
-    , packCompression 
-    , predictorOfConstant
-    ) where
-
-#if !MIN_VERSION_base(4,8,0)
-import Control.Applicative( (<$>), (<*>), pure )
-#endif
-
-import Control.Monad( forM_, when, replicateM, )
-import Data.Bits( (.&.), unsafeShiftR )
-import Data.Binary( Binary( .. ) )
-import Data.Binary.Get( Get
-                      , getWord16le, getWord16be
-                      , getWord32le, getWord32be
-                      , bytesRead
-                      , skip
-                      , getByteString
-                      )
-import Data.Binary.Put( Put
-                      , putWord16le, putWord16be
-                      , putWord32le, putWord32be
-                      , putByteString
-                      )
-import Data.Function( on )
-import Data.List( sortBy, mapAccumL )
-import qualified Data.Vector as V
-import qualified Data.ByteString as B
-import Data.Int( Int32 )
-import Data.Word( Word8, Word16, Word32 )
-
-import Codec.Picture.Metadata.Exif
-{-import Debug.Trace-}
-
-data Endianness
-  = EndianLittle
-  | EndianBig
-  deriving (Eq, Show)
-
-instance Binary Endianness where
-  put EndianLittle = putWord16le 0x4949
-  put EndianBig = putWord16le 0x4D4D
-
-  get = do
-    tag <- getWord16le
-    case tag of
-      0x4949 -> return EndianLittle
-      0x4D4D -> return EndianBig
-      _ -> fail "Invalid endian tag value"
-
--- | Because having a polymorphic get with endianness is to nice
--- to pass on, introducing this helper type class, which is just
--- a superset of Binary, but formalising a parameter passing
--- into it.
-class BinaryParam a b where
-  getP :: a -> Get b
-  putP :: a -> b -> Put
-
-data TiffHeader = TiffHeader
-  { hdrEndianness :: !Endianness
-  , hdrOffset     :: {-# UNPACK #-} !Word32
-  }
-  deriving (Eq, Show)
-
-instance BinaryParam Endianness Word16 where
-  putP EndianLittle = putWord16le
-  putP EndianBig = putWord16be
-
-  getP EndianLittle = getWord16le
-  getP EndianBig = getWord16be
-
-instance BinaryParam Endianness Int32 where
-  putP en v = putP en $ (fromIntegral v :: Word32)
-  getP en = fromIntegral <$> (getP en :: Get Word32) 
-
-instance BinaryParam Endianness Word32 where
-  putP EndianLittle = putWord32le
-  putP EndianBig = putWord32be
-
-  getP EndianLittle = getWord32le
-  getP EndianBig = getWord32be
-
-instance Binary TiffHeader where
-  put hdr = do
-    let endian = hdrEndianness hdr
-    put endian
-    putP endian (42 :: Word16)
-    putP endian $ hdrOffset hdr
-
-  get = do
-    endian <- get
-    magic <- getP endian
-    let magicValue = 42 :: Word16
-    when (magic /= magicValue)
-         (fail "Invalid TIFF magic number")
-    TiffHeader endian <$> getP endian
-
-data TiffPlanarConfiguration
-  = PlanarConfigContig    -- = 1
-  | PlanarConfigSeparate  -- = 2
-
-planarConfgOfConstant :: Word32 -> Get TiffPlanarConfiguration
-planarConfgOfConstant 0 = pure PlanarConfigContig
-planarConfgOfConstant 1 = pure PlanarConfigContig
-planarConfgOfConstant 2 = pure PlanarConfigSeparate
-planarConfgOfConstant v = fail $ "Unknown planar constant (" ++ show v ++ ")"
-
-constantToPlaneConfiguration :: TiffPlanarConfiguration -> Word16
-constantToPlaneConfiguration PlanarConfigContig = 1
-constantToPlaneConfiguration PlanarConfigSeparate = 2
-
-data TiffCompression
-  = CompressionNone           -- 1
-  | CompressionModifiedRLE    -- 2
-  | CompressionLZW            -- 5
-  | CompressionJPEG           -- 6
-  | CompressionPackBit        -- 32273
-
-data IfdType
-  = TypeByte
-  | TypeAscii
-  | TypeShort
-  | TypeLong
-  | TypeRational
-  | TypeSByte
-  | TypeUndefined
-  | TypeSignedShort
-  | TypeSignedLong
-  | TypeSignedRational
-  | TypeFloat
-  | TypeDouble
-  deriving Show
-
-instance BinaryParam Endianness IfdType where
-    getP endianness = getP endianness >>= conv where
-      conv :: Word16 -> Get IfdType
-      conv v = case v of
-        1  -> return TypeByte
-        2  -> return TypeAscii
-        3  -> return TypeShort
-        4  -> return TypeLong
-        5  -> return TypeRational
-        6  -> return TypeSByte
-        7  -> return TypeUndefined
-        8  -> return TypeSignedShort
-        9  -> return TypeSignedLong
-        10 -> return TypeSignedRational
-        11 -> return TypeFloat
-        12 -> return TypeDouble
-        _  -> fail "Invalid TIF directory type"
-
-    putP endianness = putP endianness . conv where
-      conv :: IfdType -> Word16
-      conv v = case v of
-        TypeByte -> 1
-        TypeAscii -> 2
-        TypeShort -> 3
-        TypeLong -> 4
-        TypeRational -> 5
-        TypeSByte -> 6
-        TypeUndefined -> 7
-        TypeSignedShort -> 8
-        TypeSignedLong -> 9
-        TypeSignedRational -> 10
-        TypeFloat -> 11
-        TypeDouble -> 12
-
-instance BinaryParam Endianness ExifTag where
-  getP endianness = tagOfWord16 <$> getP endianness
-  putP endianness = putP endianness . word16OfTag
-
-data Predictor
-  = PredictorNone                   -- 1
-  | PredictorHorizontalDifferencing -- 2
-  deriving Eq
-
-predictorOfConstant :: Word32 -> Get Predictor
-predictorOfConstant 1 = pure PredictorNone
-predictorOfConstant 2 = pure PredictorHorizontalDifferencing
-predictorOfConstant v = fail $ "Unknown predictor (" ++ show v ++ ")"
-
-paddWrite :: B.ByteString -> Put
-paddWrite str = putByteString str >> padding where
-  zero = 0 :: Word8
-  padding = when (odd (B.length str)) $ put zero
-
-instance BinaryParam (Endianness, Int, ImageFileDirectory) ExifData where
-  putP (endianness, _, _) = dump
-    where
-      dump ExifNone = pure ()
-      dump (ExifLong _) = pure ()
-      dump (ExifShort _) = pure ()
-      dump (ExifIFD _) = pure ()
-      dump (ExifString bstr) = paddWrite bstr
-      dump (ExifUndefined bstr) = paddWrite bstr
-      -- wrong if length == 2
-      dump (ExifShorts shorts) = V.mapM_ (putP endianness) shorts
-      dump (ExifLongs longs) = V.mapM_ (putP endianness) longs
-      dump (ExifRational a b) = putP endianness a >> putP endianness b
-      dump (ExifSignedRational a b) = putP endianness a >> putP endianness b
-
-  getP (endianness, maxi, ifd) = fetcher ifd
-    where
-      align ImageFileDirectory { ifdOffset = offset } act = do
-        readed <- bytesRead
-        let delta = fromIntegral offset - readed
-        if offset >= fromIntegral maxi || fromIntegral readed > offset then
-          pure ExifNone
-        else do
-          skip $ fromIntegral delta
-          act
-
-      getE :: (BinaryParam Endianness a) => Get a
-      getE = getP endianness
-
-      getVec count = V.replicateM (fromIntegral count)
-
-      fetcher ImageFileDirectory { ifdIdentifier = TagExifOffset
-                                 , ifdType = TypeLong
-                                 , ifdCount = 1 } = do
-         align ifd $ do
-            let byOffset = sortBy (compare `on` ifdOffset)
-                cleansIfds = fmap (cleanImageFileDirectory endianness)
-            subIfds <- cleansIfds . byOffset <$> getP endianness
-            cleaned <- fetchExtended endianness maxi $ sortBy (compare `on` ifdOffset) subIfds
-            pure $ ExifIFD [(ifdIdentifier fd, ifdExtended fd) | fd <- cleaned]
-         {-  
-      fetcher ImageFileDirectory { ifdIdentifier = TagGPSInfo
-                                 , ifdType = TypeLong
-                                 , ifdCount = 1 } = do
-         align ifd 
-         subIfds <- fmap (cleanImageFileDirectory endianness) <$> getP endianness
-         cleaned <- fetchExtended endianness subIfds
-         pure $ ExifIFD [(ifdIdentifier fd, ifdExtended fd) | fd <- cleaned]
-        -}
-      fetcher ImageFileDirectory { ifdType = TypeUndefined, ifdCount = count } | count > 4 =
-         align ifd $ ExifUndefined <$> getByteString (fromIntegral count)
-      fetcher ImageFileDirectory { ifdType = TypeUndefined, ifdOffset = ofs } =
-          pure . ExifUndefined . B.pack $ take (fromIntegral $ ifdCount ifd)
-              [fromIntegral $ ofs .&. 0xFF000000 `unsafeShiftR` (3 * 8)
-              ,fromIntegral $ ofs .&. 0x00FF0000 `unsafeShiftR` (2 * 8)
-              ,fromIntegral $ ofs .&. 0x0000FF00 `unsafeShiftR` (1 * 8)
-              ,fromIntegral $ ofs .&. 0x000000FF
-              ]
-      fetcher ImageFileDirectory { ifdType = TypeAscii, ifdCount = count } | count > 1 =
-          align ifd $ ExifString <$> getByteString (fromIntegral count)
-      fetcher ImageFileDirectory { ifdType = TypeShort, ifdCount = 2, ifdOffset = ofs } =
-          pure . ExifShorts $ V.fromListN 2 valList
-            where high = fromIntegral $ ofs `unsafeShiftR` 16
-                  low = fromIntegral $ ofs .&. 0xFFFF
-                  valList = case endianness of
-                    EndianLittle -> [low, high]
-                    EndianBig -> [high, low]
-      fetcher ImageFileDirectory { ifdType = TypeRational, ifdCount = 1 } = do
-          align ifd $ ExifRational <$> getP EndianLittle <*> getP EndianLittle
-      fetcher ImageFileDirectory { ifdType = TypeSignedRational, ifdCount = 1 } = do
-          align ifd $ ExifSignedRational <$> getP EndianLittle <*> getP EndianLittle
-      fetcher ImageFileDirectory { ifdType = TypeShort, ifdCount = 1 } =
-          pure . ExifShort . fromIntegral $ ifdOffset ifd
-      fetcher ImageFileDirectory { ifdType = TypeShort, ifdCount = count } | count > 2 =
-          align ifd $ ExifShorts <$> getVec count getE
-      fetcher ImageFileDirectory { ifdType = TypeLong, ifdCount = 1 } =
-          pure . ExifLong . fromIntegral $ ifdOffset ifd
-      fetcher ImageFileDirectory { ifdType = TypeLong, ifdCount = count } | count > 1 =
-          align ifd $ ExifLongs <$> getVec count getE
-      fetcher _ = pure ExifNone
-
-cleanImageFileDirectory :: Endianness -> ImageFileDirectory -> ImageFileDirectory
-cleanImageFileDirectory EndianBig ifd@(ImageFileDirectory { ifdCount = 1 }) = aux $ ifdType ifd
-  where
-    aux TypeShort = ifd { ifdOffset = ifdOffset ifd `unsafeShiftR` 16 }
-    aux _ = ifd
-cleanImageFileDirectory _ ifd = ifd
-
-fetchExtended :: Endianness -> Int -> [ImageFileDirectory] -> Get [ImageFileDirectory]
-fetchExtended endian maxi = mapM $ \ifd -> do
-  v <- getP (endian, maxi, ifd)
-  pure $ ifd { ifdExtended = v }
-
--- | All the IFD must be written in order according to the tag
--- value of the IFD. To avoid getting to much restriction in the
--- serialization code, just sort it.
-orderIfdByTag :: [ImageFileDirectory] -> [ImageFileDirectory]
-orderIfdByTag = sortBy comparer where
-  comparer a b = compare t1 t2 where
-    t1 = word16OfTag $ ifdIdentifier a
-    t2 = word16OfTag $ ifdIdentifier b
-
--- | Given an official offset and a list of IFD, update the offset information
--- of the IFD with extended data.
-setupIfdOffsets :: Word32 -> [ImageFileDirectory] -> (Word32, [ImageFileDirectory])
-setupIfdOffsets initialOffset lst = mapAccumL updater startExtended lst
-  where ifdElementCount = fromIntegral $ length lst
-        ifdSize = 12
-        ifdCountSize = 2
-        nextOffsetSize = 4
-        startExtended = initialOffset
-                     + ifdElementCount * ifdSize
-                     + ifdCountSize + nextOffsetSize
-
-        paddedSize blob = fromIntegral $ blobLength + padding where
-          blobLength = B.length blob
-          padding = if odd blobLength then 1 else 0
-
-        updater ix ifd@(ImageFileDirectory { ifdIdentifier = TagExifOffset }) =
-            (ix, ifd { ifdOffset = ix } )
-        updater ix ifd@(ImageFileDirectory { ifdExtended = ExifUndefined b }) =
-            (ix + paddedSize b, ifd { ifdOffset = ix } )
-        updater ix ifd@(ImageFileDirectory { ifdExtended = ExifString b }) =
-            (ix + paddedSize b, ifd { ifdOffset = ix } )
-        updater ix ifd@(ImageFileDirectory { ifdExtended = ExifLongs v })
-            | V.length v > 1 = ( ix + fromIntegral (V.length v * 4)
-                               , ifd { ifdOffset = ix } )
-        updater ix ifd@(ImageFileDirectory { ifdExtended = ExifShorts v })
-            | V.length v > 2 = ( ix + fromIntegral (V.length v * 2)
-                             , ifd { ifdOffset = ix })
-        updater ix ifd = (ix, ifd)
-
-instance BinaryParam B.ByteString (TiffHeader, [[ImageFileDirectory]]) where
-  putP rawData (hdr, ifds) = do
-    put hdr
-    putByteString rawData
-    let endianness = hdrEndianness hdr
-        (_, offseted) = mapAccumL
-            (\ix ifd -> setupIfdOffsets ix $ orderIfdByTag ifd)
-            (hdrOffset hdr)
-            ifds
-    forM_ offseted $ \list -> do
-        putP endianness list
-        mapM_ (\field -> putP (endianness, (0::Int), field) $ ifdExtended field) list
-
-  getP raw = do
-    hdr <- get
-    readed <- bytesRead
-    skip . fromIntegral $ fromIntegral (hdrOffset hdr) - readed
-    let endian = hdrEndianness hdr
-        byOffset = sortBy (compare `on` ifdOffset)
-        cleanIfds = fmap (cleanImageFileDirectory endian)
-
-    ifd <-  cleanIfds . byOffset <$> getP endian
-    cleaned <- fetchExtended endian (B.length raw) ifd
-    return (hdr, [cleaned])
-
-data TiffSampleFormat
-  = TiffSampleUint
-  | TiffSampleInt
-  | TiffSampleFloat
-  | TiffSampleUnknown
-  deriving Eq
-
-unpackSampleFormat :: Word32 -> Get TiffSampleFormat
-unpackSampleFormat v = case v of
-  1 -> pure TiffSampleUint
-  2 -> pure TiffSampleInt
-  3 -> pure TiffSampleFloat
-  4 -> pure TiffSampleUnknown
-  vv -> fail $ "Undefined data format (" ++ show vv ++ ")"
-
-packSampleFormat :: TiffSampleFormat -> Word32
-packSampleFormat TiffSampleUint    = 1
-packSampleFormat TiffSampleInt     = 2
-packSampleFormat TiffSampleFloat   = 3
-packSampleFormat TiffSampleUnknown = 4
-
-data ImageFileDirectory = ImageFileDirectory
-  { ifdIdentifier :: !ExifTag -- Word16
-  , ifdType       :: !IfdType -- Word16
-  , ifdCount      :: !Word32
-  , ifdOffset     :: !Word32
-  , ifdExtended   :: !ExifData
-  }
-  deriving Show
-
-instance BinaryParam Endianness ImageFileDirectory where
-  getP endianness =
-    ImageFileDirectory <$> getE <*> getE <*> getE <*> getE
-                       <*> pure ExifNone
-        where getE :: (BinaryParam Endianness a) => Get a
-              getE = getP endianness
-
-  putP endianness ifd = do
-    let putE :: (BinaryParam Endianness a) => a -> Put
-        putE = putP endianness
-    putE $ ifdIdentifier ifd
-    putE $ ifdType ifd
-    putE $ ifdCount ifd
-    putE $ ifdOffset ifd
-
-instance BinaryParam Endianness [ImageFileDirectory] where
-  getP endianness = do
-    count <- getP endianness :: Get Word16
-    rez <- replicateM (fromIntegral count) $ getP endianness
-    _ <- getP endianness :: Get Word32
-    pure rez
-
-
-  putP endianness lst = do
-    let count = fromIntegral $ length lst :: Word16
-    putP endianness count
-    mapM_ (putP endianness) lst
-    putP endianness (0 :: Word32)
-
-data TiffColorspace
-  = TiffMonochromeWhite0 -- ^ 0
-  | TiffMonochrome       -- ^ 1
-  | TiffRGB              -- ^ 2
-  | TiffPaleted          -- ^ 3
-  | TiffTransparencyMask -- ^ 4
-  | TiffCMYK             -- ^ 5
-  | TiffYCbCr            -- ^ 6
-  | TiffCIELab           -- ^ 8
-
-
-packPhotometricInterpretation :: TiffColorspace -> Word16
-packPhotometricInterpretation v = case v of
-  TiffMonochromeWhite0 -> 0
-  TiffMonochrome       -> 1
-  TiffRGB              -> 2
-  TiffPaleted          -> 3
-  TiffTransparencyMask -> 4
-  TiffCMYK             -> 5
-  TiffYCbCr            -> 6
-  TiffCIELab           -> 8
-
-unpackPhotometricInterpretation :: Word32 -> Get TiffColorspace
-unpackPhotometricInterpretation v = case v of
-  0 -> pure TiffMonochromeWhite0
-  1 -> pure TiffMonochrome
-  2 -> pure TiffRGB
-  3 -> pure TiffPaleted
-  4 -> pure TiffTransparencyMask
-  5 -> pure TiffCMYK
-  6 -> pure TiffYCbCr
-  8 -> pure TiffCIELab
-  vv -> fail $ "Unrecognized color space " ++ show vv
-
-data ExtraSample
-  = ExtraSampleUnspecified       -- ^ 0
-  | ExtraSampleAssociatedAlpha   -- ^ 1
-  | ExtraSampleUnassociatedAlpha -- ^ 2
-
-codeOfExtraSample :: ExtraSample -> Word16
-codeOfExtraSample v = case v of
-  ExtraSampleUnspecified -> 0
-  ExtraSampleAssociatedAlpha -> 1
-  ExtraSampleUnassociatedAlpha -> 2
-
-unPackCompression :: Word32 -> Get TiffCompression
-unPackCompression v = case v of
-  0 -> pure CompressionNone
-  1 -> pure CompressionNone
-  2 -> pure CompressionModifiedRLE
-  5 -> pure CompressionLZW
-  6 -> pure CompressionJPEG
-  32773 -> pure CompressionPackBit
-  vv -> fail $ "Unknown compression scheme " ++ show vv
-
-packCompression :: TiffCompression -> Word16
-packCompression v = case v of
-  CompressionNone        -> 1
-  CompressionModifiedRLE -> 2
-  CompressionLZW         -> 5
-  CompressionJPEG        -> 6
-  CompressionPackBit     -> 32773
-