packages feed

hakyll-images 0.4.1 → 0.4.2

raw patch · 11 files changed

+200/−132 lines, 11 filessetup-changed

Files

CHANGELOG.md view
@@ -1,31 +1,35 @@-# Change log--## Release 0.4.1--* Added some regression tests-* Simplified type architecture (no surface changes)--## Release 0.4--* Fixed an issue from version 0.3.1 where some type instances were missing to write images to disk.--## Release 0.3.1--* Change underlying image type to carry image format around.--## Release 0.3--* Refactored the internal structure to allow for composition of compilers--## Release 0.1.1--* Exposed `resizeImageCompiler` and `scaleImageCompiler` to the base `Hakyll.Images` module--## Release 0.1.0--* added `resizeImageCompiler` to resize images to a specific shape;-* added `scaleImageCompiler` to scale images while keeping aspect ratio.--## Release 0.0.1--* Added compressJpgCompiler to compress JPEGs.+# Change log
+
+## Release 0.4.2 (development)
+
+* Added `ensureFitCompiler`, a Hakyll compiler much like `scaleImageCompiler` but that will only scale images down.
+
+## Release 0.4.1
+
+* Added some regression tests
+* Simplified type architecture (no surface changes)
+
+## Release 0.4
+
+* Fixed an issue from version 0.3.1 where some type instances were missing to write images to disk.
+
+## Release 0.3.1
+
+* Change underlying image type to carry image format around.
+
+## Release 0.3
+
+* Refactored the internal structure to allow for composition of compilers
+
+## Release 0.1.1
+
+* Exposed `resizeImageCompiler` and `scaleImageCompiler` to the base `Hakyll.Images` module
+
+## Release 0.1.0
+
+* added `resizeImageCompiler` to resize images to a specific shape;
+* added `scaleImageCompiler` to scale images while keeping aspect ratio.
+
+## Release 0.0.1
+
+* Added compressJpgCompiler to compress JPEGs.
LICENSE.md view
@@ -1,11 +1,11 @@-Copyright 2019 Laurent P. René de Cotret--Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:--1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.--2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.--3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.-+Copyright 2019 Laurent P. René de Cotret
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
README.md view
@@ -21,8 +21,6 @@                             , scaleImageCompiler
                             )
 
-(...)
-
 hakyll $ do
 
     -- Compress all source Jpegs to a Jpeg quality of 50 (maximum of 100)
@@ -37,7 +35,7 @@         route idRoute
         compile $ loadImage 
             >>= resizeImageCompiler 64 48
-            >>= compressJpg 50
+            >>= compressJpgCompiler 50
 
     -- Scale images to fit within a 600x400 box
     -- Aspect ratio will be preserved
@@ -49,7 +47,7 @@     (...)
 ```
 
-Take a look at the [documentation](hackage.haskell.org/package/hakyll-images) for more usage examples.
+Take a look at the [documentation](https://hackage.haskell.org/package/hakyll-images) for more usage examples.
 
 If you would like a feature added, consider creating [an issue on Github](https://github.com/LaurentRDC/hakyll-images/issues/)
 
Setup.hs view
@@ -1,7 +1,7 @@--- This script is used to build and install your package. Typically you don't--- need to change it. The Cabal documentation has more information about this--- file: <https://www.haskell.org/cabal/users-guide/installing-packages.html>.-import qualified Distribution.Simple--main :: IO ()-main = Distribution.Simple.defaultMain+-- This script is used to build and install your package. Typically you don't
+-- need to change it. The Cabal documentation has more information about this
+-- file: <https://www.haskell.org/cabal/users-guide/installing-packages.html>.
+import qualified Distribution.Simple
+
+main :: IO ()
+main = Distribution.Simple.defaultMain
hakyll-images.cabal view
@@ -1,15 +1,15 @@ cabal-version: 1.12
 --- This file has been generated from package.yaml by hpack version 0.30.0.+-- This file has been generated from package.yaml by hpack version 0.31.1. -- -- see: https://github.com/sol/hpack ----- hash: 8668d79c72dd31f545171175bf9caadb045ee31f00b196459adfa05a3c45a1b8+-- hash: 547b36dd9930c6d6024668fa4e252f159f4c59a719d63f0bc7c13a0d7371b642  name:           hakyll-images-version:        0.4.1+version:        0.4.2 synopsis:       Hakyll utilities to work with images-description:    hakyll-images is an add-on to the hakyll package. It adds utilities to work with images, including JPEG compression.+description:    hakyll-images is an add-on to the hakyll package. It adds utilities to work with images, including JPEG compression, image scaling, and more. category:       Web homepage:       https://github.com/LaurentRDC/hakyll-images#readme bug-reports:    https://github.com/LaurentRDC/hakyll-images/issues
library/Hakyll/Images.hs view
@@ -67,6 +67,8 @@     , resizeImageCompiler
     , scale
     , scaleImageCompiler
+    , ensureFit
+    , ensureFitCompiler
 ) where
 
 import Hakyll.Images.CompressJpg
library/Hakyll/Images/Common.hs view
@@ -1,7 +1,7 @@-{-# LANGUAGE TypeSynonymInstances #-}
-{-# LANGUAGE FlexibleInstances    #-}
 {-# LANGUAGE DeriveDataTypeable   #-}
 {-# LANGUAGE DeriveGeneric        #-}
+{-# LANGUAGE FlexibleInstances    #-}
+{-# LANGUAGE TypeSynonymInstances #-}
 {-|
 Module      : Hakyll.Images.Common
 Description : Types and utilities for Hakyll.Images
@@ -18,24 +18,25 @@                             , encode
                             ) where
 
-import Prelude                          hiding (readFile)
+import           Prelude              hiding (readFile)
 
-import Codec.Picture.Types              (DynamicImage)
-import Codec.Picture.Saving
+import           Codec.Picture.Saving
+import           Codec.Picture.Types  (DynamicImage)
 
-import Data.Binary                      (Binary(..))
-import Data.ByteString.Lazy             (toStrict)
-import Data.ByteString                  (ByteString)
-import Data.Typeable                    (Typeable)
-import GHC.Generics                     (Generic)
+import           Data.Binary          (Binary (..))
+import           Data.ByteString      (ByteString)
+import           Data.ByteString.Lazy (toStrict)
+import           Data.Typeable        (Typeable)
+import           GHC.Generics         (Generic)
 
-import Hakyll.Core.Compiler             (Compiler, getResourceLBS, getUnderlyingExtension)
-import Hakyll.Core.Item                 (Item(..))
-import Hakyll.Core.Writable             (Writable(..))
+import           Hakyll.Core.Compiler (Compiler, getResourceLBS,
+                                       getUnderlyingExtension)
+import           Hakyll.Core.Item     (Item (..))
+import           Hakyll.Core.Writable (Writable (..))
 
 -- Supported (i.e. encodable) image formats
 data ImageFormat
-    = Jpeg 
+    = Jpeg
     | Png
     | Bitmap
     | Tiff
@@ -47,7 +48,7 @@ -- Polymorphic type only to get an instance of functor.
 -- Do not use this type.
 data Image = Image { format :: ImageFormat
-                   , image :: ByteString
+                   , image  :: ByteString
                    }
     deriving (Typeable)
 
@@ -68,7 +69,7 @@ -- @
 -- match "*.jpg" $ do
 --     route idRoute
---     compile $ loadImage 
+--     compile $ loadImage
 --         >>= compressJpgCompiler 50
 -- @
 loadImage :: Compiler (Item Image)
@@ -94,4 +95,4 @@ encode Jpeg im   = Image Jpeg   $ (toStrict . imageToJpg 100) im
 encode Png im    = Image Png    $ (toStrict . imageToPng) im
 encode Bitmap im = Image Bitmap $ (toStrict . imageToBitmap) im
-encode Tiff im   = Image Tiff   $ (toStrict . imageToTiff) im+encode Tiff im   = Image Tiff   $ (toStrict . imageToTiff) im
library/Hakyll/Images/CompressJpg.hs view
@@ -19,7 +19,7 @@     import Hakyll.Images        ( loadImage
                                 , compressJpgCompiler
                                 )
-    
+
     hakyll $ do
 
         -- Compress all source Jpegs to a Jpeg quality of 50
@@ -27,7 +27,7 @@             route idRoute
             compile $ loadImage
                 >>= compressJpgCompiler 50
-        
+
         (... omitted ...)
 @
 -}
@@ -37,19 +37,16 @@     , compressJpg
     ) where
 
-import Data.ByteString.Lazy             (toStrict)
+import           Data.ByteString.Lazy (toStrict)
 
-import Codec.Picture.Jpg                (decodeJpeg)
-import Codec.Picture.Saving             (imageToJpg)
+import           Codec.Picture.Jpg    (decodeJpeg)
+import           Codec.Picture.Saving (imageToJpg)
 
-import Hakyll.Core.Item                 (Item(..))
-import Hakyll.Core.Compiler             (Compiler)
+import           Hakyll.Core.Compiler (Compiler)
+import           Hakyll.Core.Item     (Item (..))
 
-import Hakyll.Images.Common             ( Image(..)
-                                        , ImageFormat(..)
-                                        , image
-                                        , format
-                                        )
+import           Hakyll.Images.Common (Image (..), ImageFormat (..), format,
+                                       image)
 
 
 -- | Jpeg encoding quality, from 0 (lower quality) to 100 (best quality).
@@ -58,15 +55,15 @@ 
 -- | Compress a JPG bytestring to a certain quality setting.
 -- The quality should be between 0 (lowest quality) and 100 (best quality).
--- An error is raised if the image cannot be decoded, or if the 
+-- An error is raised if the image cannot be decoded, or if the
 -- encoding quality is out-of-bounds
 compressJpg :: JpgQuality -> Image -> Image
 compressJpg quality src = if (format src) /= Jpeg
         then error $ "Image is not a JPEG."
-        else 
+        else
             case decodeJpeg $ image src of
                 Left _         -> error $ "Loading the image failed."
-                Right dynImage -> 
+                Right dynImage ->
                     if (quality < 0 || quality > 100)
                         then error $ "JPEG encoding quality should be between 0 and 100."
                         else Image Jpeg $ (toStrict $ imageToJpg quality dynImage)
@@ -78,8 +75,8 @@ -- @
 -- match "*.jpg" $ do
 --     route idRoute
---     compile $ loadImage 
+--     compile $ loadImage
 --         >>= compressJpgCompiler 50
 -- @
 compressJpgCompiler :: JpgQuality -> Item Image -> Compiler (Item Image)
-compressJpgCompiler quality item = return $ compressJpg quality <$> item +compressJpgCompiler quality item = return $ compressJpg quality <$> item
library/Hakyll/Images/Resize.hs view
@@ -8,19 +8,19 @@ Stability   : unstable
 Portability : portable
 
-This module defines two Hakyll compilers. The first one, 'resizeImageCompiler', 
+This module defines two Hakyll compilers. The first one, 'resizeImageCompiler',
 is used to resize images to specific dimensions. The aspect ratio might not be the same.
 
-The other compiler, `scaleImageCompiler`, scales images to fit within a specified 
+The other compiler, `scaleImageCompiler`, scales images to fit within a specified
 box while preserving aspect ratio.
 
 @
     import Hakyll
     import Hakyll.Images        ( loadImage
-                                , resizeImageCompiler 
+                                , resizeImageCompiler
                                 , scaleImageCompiler
                                 )
-    
+
     hakyll $ do
 
         -- Resize all profile pictures with .png extensions to 64x48
@@ -28,13 +28,13 @@             route idRoute
             compile $ loadImage
                 >>= resizeImageCompiler 64 48
-        
+
         -- Scale images to fit within a 600x400 box
         match "images/**" $ do
             route idRoute
             compile $ loadImage
                 >>= scaleImageCompiler 600 400
-        
+
         (... omitted ...)
 @
 -}
@@ -44,27 +44,30 @@     , resizeImageCompiler
     , scale
     , scaleImageCompiler
+    , ensureFit
+    , ensureFitCompiler
     ) where
 
-import Codec.Picture            (convertRGBA8, decodeImage)
-import Codec.Picture.Types      (DynamicImage(..), imageHeight, imageWidth)
-import Codec.Picture.Extra      (scaleBilinear)
+import           Codec.Picture        (convertRGBA8, decodeImage)
+import           Codec.Picture.Extra  (scaleBilinear)
+import           Codec.Picture.Types  (DynamicImage (..), imageHeight,
+                                       imageWidth)
 
-import Data.ByteString          (ByteString)
-import Data.Ratio               ((%))
+import           Data.ByteString      (ByteString)
+import           Data.Ratio           ((%))
 
-import Hakyll.Core.Item         (Item(..))
-import Hakyll.Core.Compiler     (Compiler)
+import           Hakyll.Core.Compiler (Compiler)
+import           Hakyll.Core.Item     (Item (..))
 
-import Hakyll.Images.Common     (Image(..), encode)
+import           Hakyll.Images.Common (Image (..), encode)
 
 type Width = Int
 type Height = Int
 
 decodeImage' :: ByteString -> DynamicImage
 decodeImage' im = case decodeImage im of
-    Left msg -> error msg
-    Right im' -> im' 
+    Left msg  -> error msg
+    Right im' -> im'
 
 -- | Resize an image to specified width and height using the bilinear transform.
 -- The aspect ratio may not be respected.
@@ -74,50 +77,96 @@ resize :: Width -> Height -> DynamicImage -> DynamicImage
 resize w h = ImageRGBA8 . (scaleBilinear w h) . convertRGBA8
 
+-- | Scale an image to a size that will fit in the specified width and height,
+-- while preserving aspect ratio. Images might be scaled up as well.
+--
+-- In the process, an image is converted to RGBA8. Therefore, some information
+-- loss may occur.
+--
+-- To scale images down only, take a look at 'ensureFit'.
+scale :: Width -> Height -> DynamicImage -> DynamicImage
+scale w h = scale' w h True
+
+-- | Scale an image to a size that will fit in the specified width and height,
+-- while preserving aspect ratio. Images might be scaled up as well.
+--
+-- In the process, an image is converted to RGBA8. Therefore, some information
+-- loss may occur.
+scale' :: Width         -- ^ Desired width.
+       -> Height        -- ^ Desired height.
+       -> Bool          -- ^ Allow scaling up as well.
+       -> DynamicImage  -- ^ Source image
+       -> DynamicImage  -- ^ Scaled image.
+scale' w h upAllowed img = resize maxWidth maxHeight img
+    where
+        img' = convertRGBA8 img -- Required to extract height and width
+        (imgWidth, imgHeight) = (imageWidth img', imageHeight img')
+        -- Find the smallest resizing that will accomodate both the width
+        -- and height.
+        -- If we don't allow scaling up, minimum scaling is 1
+        resizing = if upAllowed
+            then min (w % imgWidth) (h % imgHeight)
+            else minimum [w % imgWidth, h % imgHeight, 1]
+        maxWidth = round (resizing * fromIntegral imgWidth)
+        maxHeight = round (resizing * fromIntegral imgHeight)
+
+-- | Scale an image down to a size that will fit in the specified width and height,
+-- while preserving aspect ratio.
+--
+-- In the process, an image is converted to RGBA8. Therefore, some information
+-- loss may occur.
+--
+-- To scale images up __or__ down, take a look at 'scale'.
+ensureFit :: Width -> Height -> DynamicImage -> DynamicImage
+ensureFit w h = scale' w h False
+
 -- | Compiler that resizes images to a specific dimensions. Aspect ratio
 -- may not be preserved.
 --
 -- @
 -- match "*.png" $ do
 --     route idRoute
---     compile $ loadImage 
+--     compile $ loadImage
 --         >>= resizeImageCompiler 48 64
 -- @
 --
 -- Note that in the resizing process, images will be converted to RGBA8.
+-- To preserve aspect ratio, take a look at 'scaleImageCompiler'.
 resizeImageCompiler :: Width -> Height -> Item Image -> Compiler (Item Image)
 resizeImageCompiler w h item =
     let fmt = (format . itemBody) item
     in return $ (encode fmt . resize w h . decodeImage' . image) <$> item
 
--- | Scale an image to a size that will fit in the specified width and height,
--- while preserving aspect ratio.
--- 
--- In the process, an image is converted to RGBA8. Therefore, some information
--- loss may occur.
-scale :: Width -> Height -> DynamicImage -> DynamicImage
-scale w h img = resize maxWidth maxHeight img
-    where
-        img' = convertRGBA8 img -- Required to extract height and width
-        (imgWidth, imgHeight) = (imageWidth img', imageHeight img')
-        -- Find the smallest resizing that will accomodate both the width 
-        -- and height.
-        resizing = min (w % imgWidth) (h % imgHeight)
-        maxWidth = round (resizing * fromIntegral imgWidth)
-        maxHeight = round (resizing * fromIntegral imgHeight)
-
 -- | Compiler that rescales images to fit within dimensions. Aspect ratio
--- will be preserved.
+-- will be preserved. Images might be scaled up as well.
 --
 -- @
 -- match "*.tiff" $ do
 --     route idRoute
---     compile $ loadImage 
+--     compile $ loadImage
 --         >>= scaleImageCompiler 48 64
 -- @
 --
 -- Note that in the resizing process, images will be converted to RGBA8.
+-- To ensure images are only scaled __down__, take a look at 'ensureFitCompiler'.
 scaleImageCompiler :: Width -> Height -> Item Image -> Compiler (Item Image)
 scaleImageCompiler w h item =
     let fmt = (format . itemBody) item
-    in return $ (encode fmt . scale w h . decodeImage' . image) <$> item+    in return $ (encode fmt . scale w h . decodeImage' . image) <$> item
+
+-- | Compiler that ensures images will fit within dimensions. Images might
+-- be scaled down, but never up.  Aspect ratio will be preserved.
+--
+-- @
+-- match "*.tiff" $ do
+--     route idRoute
+--     compile $ loadImage
+--         >>= ensureFitCompiler 48 64
+-- @
+--
+-- Note that in the resizing process, images will be converted to RGBA8.
+-- To allow the possibility of scaling up, take a look at 'scaleImageCompiler'.
+ensureFitCompiler :: Width -> Height -> Item Image -> Compiler (Item Image)
+ensureFitCompiler w h item  =
+    let fmt = (format . itemBody) item
+    in return $ (encode fmt . ensureFit w h . decodeImage' . image) <$> item
package.yaml view
@@ -1,5 +1,5 @@ name: hakyll-images
-version: '0.4.1'
+version: '0.4.2'
 github: "LaurentRDC/hakyll-images"
 license: BSD3
 author: "Laurent P. René de Cotret"
@@ -7,7 +7,7 @@ synopsis: "Hakyll utilities to work with images"
 description: 
   hakyll-images is an add-on to the hakyll package. It adds utilities to work
-  with images, including JPEG compression. 
+  with images, including JPEG compression, image scaling, and more. 
 category: Web
 license-file: LICENSE.md
 
tests/Hakyll/Images/Resize/Tests.hs view
@@ -11,7 +11,7 @@ 
 
 --------------------------------------------------------------------------------
-import           Hakyll.Images.Resize
+import           Hakyll.Images
 import qualified Data.ByteString        as B
 import           Data.Ratio             ((%))
 
@@ -57,6 +57,20 @@     assertEqual "Image width was not scaled properly" width 600
     assertBool "Image height was not scaled property" (height<= 400)
 
+-- Test that the images that already fit in dimensions are not scaled
+testEnsureFit :: Assertion
+testEnsureFit = do
+    image <- testJpg
+    let (originalWidth, originalHeight) = (imageWidth . convertRGBA8 $ image, imageHeight . convertRGBA8 $ image)
+        scaledImage = ensureFit 2000 2000 image
+        converted = convertRGBA8 scaledImage
+        (width, height) = ( imageWidth converted
+                          , imageHeight converted
+                          )
+    assertEqual "Image width was not scaled properly" width originalWidth
+    assertEqual "Image height was not scaled property" height originalHeight
+
+
 -- Test that the rescaled image has the same aspect ratio
 testScalePreservesAspectRatio :: Assertion
 testScalePreservesAspectRatio = do
@@ -73,8 +87,11 @@ tests :: TestTree
 tests = testGroup "Hakyll.Images.Resize.Tests" $ concat
    [ fromAssertions "rescale" 
-        [ testResize 
-        , testScale
+        [ testScale
         , testScalePreservesAspectRatio
-        ] 
+        ]
+    , fromAssertions "ensureFit"
+        [ testEnsureFit ] 
+    , fromAssertions "resize" 
+        [ testResize ]
     ]