packages feed

netpbm 0.2.0 → 0.2.1

raw patch · 3 files changed

+133/−24 lines, 3 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

+ Graphics.Netpbm: PPMHeader :: PPMType -> Int -> Int -> PPMHeader
+ Graphics.Netpbm: data PPMHeader
+ Graphics.Netpbm: instance Eq PPMHeader
+ Graphics.Netpbm: instance Show PPMHeader
+ Graphics.Netpbm: ppmHeader :: PPM -> PPMHeader
- Graphics.Netpbm: PPM :: PPMType -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> PpmPixelData -> PPM
+ Graphics.Netpbm: PPM :: PPMHeader -> PpmPixelData -> PPM
- Graphics.Netpbm: ppmHeight :: PPM -> {-# UNPACK #-} !Int
+ Graphics.Netpbm: ppmHeight :: PPMHeader -> Int
- Graphics.Netpbm: ppmType :: PPM -> PPMType
+ Graphics.Netpbm: ppmType :: PPMHeader -> PPMType
- Graphics.Netpbm: ppmWidth :: PPM -> {-# UNPACK #-} !Int
+ Graphics.Netpbm: ppmWidth :: PPMHeader -> Int

Files

netpbm.cabal view
@@ -1,5 +1,5 @@ name:          netpbm-version:       0.2.0+version:       0.2.1 license:       MIT copyright:     2013 Niklas Hambüchen <mail@nh2.me> author:        Niklas Hambüchen <mail@nh2.me>
src/Graphics/Netpbm.hs view
@@ -13,6 +13,7 @@ , PPM (..) , PpmPixelRGB8 , PpmPixelRGB16+, PPMHeader (..) , PpmPixelData (..) , parsePPM , PpmParseResult@@ -50,15 +51,18 @@  -- | A PPM file with type, dimensions, and image data. data PPM = PPM {-  ppmType :: PPMType-, ppmWidth  :: {-# UNPACK #-} !Int-, ppmHeight :: {-# UNPACK #-} !Int+  ppmHeader :: PPMHeader , ppmData   :: PpmPixelData } +data PPMHeader = PPMHeader {+  ppmType   :: PPMType+, ppmWidth  :: Int+, ppmHeight :: Int+} deriving (Eq, Show)  instance Show PPM where-  show PPM { ppmType, ppmWidth, ppmHeight } = "PPM " ++ show ppmType ++ " image " ++ dim+  show PPM { ppmHeader = PPMHeader { ppmType, ppmWidth, ppmHeight } } = "PPM " ++ show ppmType ++ " image " ++ dim     where       dim = show (ppmWidth, ppmHeight) @@ -162,9 +166,11 @@   skipSpace   comments   width <- decimalC+  comments   skipSpace   comments   height <- decimalC+  comments   skipSpace   comments   maxColorVal <- decimalC@@ -174,12 +180,13 @@   _ <- A8.satisfy isSpace -- obligatory SINGLE whitespace   -- Starting from here, comments are not allowed any more   raster <- if maxColorVal < 256 -- 1 or 2 bytes per pixel+      -- TODO check if values are smaller than maxColorVal       then PpmPixelDataRGB8 <$> (U.replicateM (height * width) $              PpmPixelRGB8 <$> anyWord8 <*> anyWord8 <*> anyWord8)       else PpmPixelDataRGB16 <$> (U.replicateM (height * width) $              PpmPixelRGB16 <$> anyWord16be <*> anyWord16be <*> anyWord16be) -  return $ PPM ppmType width height raster+  return $ PPM (PPMHeader ppmType width height) raster    where     isValidColorVal v = v > 0 && v < 65536@@ -187,10 +194,10 @@     comment = "#" .*> A.takeWhile isNotNewline <* endOfLine     isNotNewline w = w /= 10 && w /= 13     -- Decimal, possibly with comments interleaved,-    -- but starting with a digit.+    -- but starting and ending with a digit.     -- See the notes about comments above.     decimalC :: Parser Int-    decimalC = foldl' shiftDecimalChar 0 <$> many1' (digit <* comments)+    decimalC = foldl' shiftDecimalChar 0 <$> ((:) <$> digit <*> many (comments *> digit))     shiftDecimalChar a d = a * 10 + ord d - (48 :: Int)  @@ -219,14 +226,15 @@ -- and potentially an unparsable rest input. parsePPM :: ByteString -> PpmParseResult parsePPM bs = case parse imagesParser bs of-  Done ""   images -> Right (images, Nothing)-  Done rest images -> Right (images, Just rest)-  Fail _ _ e       -> Left e   -- The image file ByteStrings are not terminated by '\0',   -- so Attoparsec will issue a Partial result when it   -- parses to EOF. Passing in "" signalizes EOF.-  Partial cont -> case cont "" of-    Done ""   images -> Right (images, Nothing)-    Done rest images -> Right (images, Just rest)-    Partial _        -> error "parsePPM bug: Got a partial result after end of input"-    Fail _ _ e       -> Left e+  Partial cont -> resultToEither (cont "")+  r            -> resultToEither r+  where+    -- Assumes a Partial result has already been fed with "" (another Partial cannot happen)+    resultToEither r = case r of+      Done ""   images -> Right (images, Nothing)+      Done rest images -> Right (images, Just rest)+      Partial _        -> error "parsePPM bug: Got a partial result after end of input"+      Fail _ cs e      -> Left $ e ++ "; contexts: " ++ show cs
test/Main.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE NamedFieldPuns, OverloadedStrings #-}  import qualified Data.ByteString as BS import           Control.Applicative@@ -11,17 +11,23 @@  checkSinglePPM :: PPMType -> (Int, Int) -> PpmParseResult -> Assertion checkSinglePPM typ size parseResult = case parseResult of-  Right ([PPM { ppmType, ppmWidth, ppmHeight }], rest) -> (ppmType, (ppmWidth, ppmHeight), rest) `shouldBe` (typ, size, Nothing)+  Right ([PPM { ppmHeader = PPMHeader { ppmType, ppmWidth, ppmHeight } }], rest) -> (ppmType, (ppmWidth, ppmHeight), rest) `shouldBe` (typ, size, Nothing)   Right (ppms, _)                                      -> assertFailure $ "expected only one image, but got " ++ show (length ppms)-  _                                                    -> assertFailure "image parse failed"+  Left e                                               -> assertFailure $ "image parse failed: " ++ e  +shouldNotParse :: PpmParseResult -> Assertion+shouldNotParse res = case res of+  Left _ -> return ()+  Right r -> assertFailure $ "should not parse, but parses as: " ++ show r++ parse :: FilePath -> IO PpmParseResult parse f = parsePPM <$> BS.readFile f   parseTestFile :: String -> String -> (PpmParseResult -> Assertion) -> Spec-parseTestFile name desc check = it ("parses " ++ desc ++ " (" ++ name ++ ")") $+parseTestFile name desc check = it (desc ++ " (" ++ name ++ ")") $   parse ("test/ppms/" ++ name) >>= check  @@ -41,8 +47,103 @@     parseTestFile "testimg.ppm" "the color file from the netpbm test suite" $       checkSinglePPM P6 (227,149) -    parseTestFile "face.ppm" "a PPM with a trailing newline" $-      checkSinglePPM P6 (512,512)+    describe "more test files from the internet" $ do+      forM_+        [ ("boxes_1.ppm", (63,63))+        , ("boxes_2.ppm", (63,63))+        , ("house_1.ppm", (111,132))+        , ("house_2.ppm", (111,132))+        , ("moreboxes_1.ppm", (63,63))+        , ("moreboxes_2.ppm", (63,63))+        , ("sign_1.ppm", (99,99))+        , ("sign_2.ppm", (99,99))+        , ("stop_1.ppm", (99,99))+        , ("stop_2.ppm", (99,99))+        , ("synth_1.ppm", (100,100))+        , ("synth_2.ppm", (100,100))+        , ("tree_1.ppm", (133,133))+        , ("tree_2.ppm", (133,133))+        , ("west_1.ppm", (366,216))+        , ("west_2.ppm", (366,216))+        ] $ \(f, size) ->+          parseTestFile ("internet/set1/" ++ f) "from the internet" $+            checkSinglePPM P6 size -    -- parseTestFile "gitlogo-double.ppm" "a multi-image file" $ do-    --   checkSinglePPM P6 (220,92)++    parseTestFile "gitlogo-double.ppm" "a multi-image file" $ do+      \res -> case res of+        Right ([ PPM { ppmHeader = h1 }+               , PPM { ppmHeader = h2 }], rest) -> do h1 `shouldBe` PPMHeader P6 220 92+                                                      h2 `shouldBe` PPMHeader P6 220 92+                                                      rest `shouldBe` Nothing+        Right r                                    -> assertFailure $ "parsed unexpected: " ++ show r+        Left e                                     -> assertFailure $ "did not parse: " ++ e+++    describe "comments" $ do++      parseTestFile "gitlogo-comments.ppm" "comments as a sane user would write them" $+        checkSinglePPM P6 (220,92)++      parseTestFile "gitlogo-comment-after-magic-number.ppm" "a comment directly after the P6" $+        checkSinglePPM P6 (220,92)++      parseTestFile "gitlogo-only-spaces-in-header.ppm" "only spaces as header separators" $+        checkSinglePPM P6 (220,92)++      parseTestFile "gitlogo-comment-is-data.ppm" "the user thinks they wrote a comment, but it's actually parsed as data" $+        \res -> case res of+          Right ([PPM { ppmHeader }], Just rest) -> do ppmHeader `shouldBe` PPMHeader P6 220 92+                                                       rest `shouldBe` "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\n"+          Right r                                -> assertFailure $ "parsed unexpected: " ++ show r+          Left e                                 -> assertFailure $ "did not parse: " ++ e+++    describe "weird files that are still OK with the spec" $ do++      parseTestFile "weird/gitlogo-width-0.ppm" "width '00' set in an image" $+        \res -> case res of+          Right ([PPM { ppmHeader }], Just rest) -> do ppmHeader `shouldBe` PPMHeader P6 220 0+                                                       assertBool "missing rest" (BS.length rest > 200)+          Right r                                -> assertFailure $ "parsed unexpected: " ++ show r+          Left e                                 -> assertFailure $ "did not parse: " ++ e++      parseTestFile "weird/gitlogo-comments-everywhere.ppm" "comments inside numbers" $+        checkSinglePPM P6 (220,92)+++    describe "partially valid files of which we parse as much as we can" $ do++      parseTestFile "graceful/face.ppm" "a PPM with a trailing newline" $+        checkSinglePPM P6 (512,512)++      parseTestFile "graceful/gitlogo-one-and-a-half.ppm" "a multi-image file where the second image is chopped off" $+        \res -> case res of+          Right ([PPM { ppmHeader }], Just rest) -> do ppmHeader `shouldBe` PPMHeader P6 220 92+                                                       assertBool "missing rest" (BS.length rest > 200)+          Right r                                -> assertFailure $ "parsed unexpected: " ++ show r+          Left e                                 -> assertFailure $ "did not parse: " ++ e++      parseTestFile "graceful/gitlogo-double-with-whitespace-in-between.ppm" "a multi-image file with whitespace between the images" $+        \res -> case res of+          Right ([ PPM { ppmHeader = h1 }+                 , PPM { ppmHeader = h2 }], rest) -> do h1 `shouldBe` PPMHeader P6 220 92+                                                        h2 `shouldBe` PPMHeader P6 220 92+                                                        rest `shouldBe` Nothing+          Right r                                 -> assertFailure $ "parsed unexpected: " ++ show r+          Left e                                  -> assertFailure $ "did not parse: " ++ e+++    describe "negative examples" $ do++      parseTestFile "bad/gitlogo-garbage-in-numbers.ppm" "ascii characters in a number" shouldNotParse++      parseTestFile "bad/gitlogo-width--1.ppm" "width '-1' set in an image" shouldNotParse++      parseTestFile "bad/gitlogo-not-enough-data.ppm" "not containing (width * height) bytes" shouldNotParse++      parseTestFile "bad/gitlogo-comment-in-magic-number.ppm" "comment inside magic number" shouldNotParse++      parseTestFile "bad/gitlogo-comment-user-error.ppm" "a comment accidentally being put to close to a number, eating the following whitespace" $ shouldNotParse++      parseTestFile "bad/gitlogo-comment-without-following-extra-newline-before-data-block.ppm" "no non-comment whitespace before data block" shouldNotParse