Split ConvertImage into three steps

This commit is contained in:
Niels G. W. Serup 2024-10-05 23:37:54 +02:00
parent 94c2fbfbc9
commit 9c912d2457
No known key found for this signature in database
GPG Key ID: 38EEEBCE67324F19
8 changed files with 64 additions and 23 deletions

View File

@ -33,10 +33,13 @@ module DependencyGenerator
, makeTemplate , makeTemplate
, applyTemplate , applyTemplate
, toText , toText
, convertImage
, listDirectory , listDirectory
, isDirectory , isDirectory
, readTextFile , readTextFile
, convertImage , openImage
, saveImage
, saveTextFile , saveTextFile
, copyFile , copyFile
, copyFile' , copyFile'
@ -236,6 +239,9 @@ applyTemplate a b = runFunction ApplyTemplate =<< toTupleToken a b
toText :: TokenableTo String a => a -> DepGenM' Text toText :: TokenableTo String a => a -> DepGenM' Text
toText a = runFunction ToText =<< toToken a toText a = runFunction ToText =<< toToken a
convertImage :: (TokenableTo Image a, TokenableTo ImageConversionSettings b) => a -> b -> DepGenM' Image
convertImage a b = runFunction ConvertImage =<< toTupleToken a b
listDirectory :: TokenableTo FilePath a => a -> DepGenM' [FilePath] listDirectory :: TokenableTo FilePath a => a -> DepGenM' [FilePath]
listDirectory a = runFunctionIO ListDirectory =<< toToken a listDirectory a = runFunctionIO ListDirectory =<< toToken a
@ -245,8 +251,11 @@ isDirectory a = runFunctionIO IsDirectory =<< toToken a
readTextFile :: TokenableTo FilePath a => a -> DepGenM' Text readTextFile :: TokenableTo FilePath a => a -> DepGenM' Text
readTextFile a = runFunctionIO ReadTextFile =<< toToken a readTextFile a = runFunctionIO ReadTextFile =<< toToken a
convertImage :: (TokenableTo FilePath a, TokenableTo FilePath b, TokenableTo ImageConversionSettings c) => a -> b -> c -> DepGenM () openImage :: TokenableTo FilePath a => a -> DepGenM' Image
convertImage a b c = runFunctionIO' ConvertImage =<< toTupleToken (toTupleToken a b) c openImage a = runFunctionIO OpenImage =<< toToken a
saveImage :: (TokenableTo Image a, TokenableTo FilePath b) => a -> b -> DepGenM ()
saveImage a b = runFunctionIO' SaveImage =<< toTupleToken a b
saveTextFile :: (TokenableTo Text a, TokenableTo FilePath b) => a -> b -> DepGenM () saveTextFile :: (TokenableTo Text a, TokenableTo FilePath b) => a -> b -> DepGenM ()
saveTextFile a b = runFunctionIO' SaveTextFile =<< toTupleToken a b saveTextFile a b = runFunctionIO' SaveTextFile =<< toTupleToken a b

View File

@ -5,8 +5,11 @@ module Evaluation.Function
import Prelude hiding (String, FilePath) import Prelude hiding (String, FilePath)
import qualified Prelude import qualified Prelude
import Types (Function(..), Value(..), String(..), Template(..), fromValue) import Types.Values
import Types (Function(..), Value(..), fromValue, makeString, makeImage)
import qualified Codec.Picture as CP
import qualified Codec.Picture.STBIR as CPS
import Data.Char (toLower) import Data.Char (toLower)
import qualified Data.Text as T import qualified Data.Text as T
@ -17,9 +20,6 @@ fileComponents s =
_ : firstRev' -> (reverse firstRev', reverse lastRev) _ : firstRev' -> (reverse firstRev', reverse lastRev)
[] -> (reverse lastRev, "") [] -> (reverse lastRev, "")
makeString :: Prelude.String -> Value
makeString = String . StringWrapper
unStringWrapper :: Value -> Prelude.String unStringWrapper :: Value -> Prelude.String
unStringWrapper = \case unStringWrapper = \case
String (StringWrapper s) -> s String (StringWrapper s) -> s
@ -63,5 +63,12 @@ evalFunction f x = case (f, x) of
(ToText, String (StringWrapper s)) -> (ToText, String (StringWrapper s)) ->
Text $ T.pack s Text $ T.pack s
(ConvertImage, Tuple (Image (ImageWrapper image),
ImageConversionSettings (ResizeToWidth widthResized))) ->
let sizeFactor :: Double
sizeFactor = fromIntegral (CP.imageWidth image) / fromIntegral widthResized
heightResized = round (fromIntegral (CP.imageHeight image) / sizeFactor)
in makeImage $ CPS.resize CPS.defaultOptions widthResized heightResized image
_ -> _ ->
error "unexpected combination of function and argument type" error "unexpected combination of function and argument type"

View File

@ -5,14 +5,13 @@ module Evaluation.FunctionIO
import Prelude hiding (String, FilePath) import Prelude hiding (String, FilePath)
import Types.Values import Types.Values
import Types (FunctionIO(..), Value(..), toValue) import Types (FunctionIO(..), Value(..), toValue, makeImage)
import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy as TL
import qualified Data.Text.IO as T import qualified Data.Text.IO as T
import qualified Text.Pandoc as P import qualified Text.Pandoc as P
import qualified Text.Blaze.Html.Renderer.Text as B import qualified Text.Blaze.Html.Renderer.Text as B
import qualified Codec.Picture as CP import qualified Codec.Picture as CP
import qualified Codec.Picture.STBIR as CPS
import System.Directory (listDirectory, doesDirectoryExist, createDirectory, copyFile) import System.Directory (listDirectory, doesDirectoryExist, createDirectory, copyFile)
evalFunctionIO :: FunctionIO -> Value -> IO Value evalFunctionIO :: FunctionIO -> Value -> IO Value
@ -26,17 +25,14 @@ evalFunctionIO f x = case (f, x) of
(ReadTextFile, String (StringWrapper s)) -> (ReadTextFile, String (StringWrapper s)) ->
Text <$> T.readFile s Text <$> T.readFile s
(ConvertImage, Tuple (Tuple (String (StringWrapper source), String (StringWrapper target)), (OpenImage, String (StringWrapper s)) -> do
ImageConversionSettings (ResizeToWidth widthResized))) -> do imageOrig <- CP.readImage s
imageOrig <- CP.readImage source case imageOrig of
let imageOrig' = case imageOrig of Left e -> error ("unexpected error: " ++ e)
Left s -> error ("unexpected error: " ++ s) Right image -> pure $ makeImage $ CP.convertRGB8 image
Right image -> CP.convertRGB8 image
sizeFactor :: Double (SaveImage, Tuple (Image (ImageWrapper image), String (StringWrapper s))) -> do
sizeFactor = fromIntegral (CP.imageWidth imageOrig') / fromIntegral widthResized CP.saveJpgImage 90 s $ CP.ImageRGB8 image
heightResized = round (fromIntegral (CP.imageHeight imageOrig') / sizeFactor)
imageResized = CPS.resize CPS.defaultOptions widthResized heightResized imageOrig'
CP.saveJpgImage 90 target $ CP.ImageRGB8 imageResized
pure Empty pure Empty
(SaveTextFile, Tuple (Text t, String (StringWrapper s))) -> do (SaveTextFile, Tuple (Text t, String (StringWrapper s))) -> do

View File

@ -18,7 +18,8 @@ handleRecipeDir outputDir htmlTemplate indexName dir = do
path `copyTo` outputDir path `copyTo` outputDir
(base, ext) <- untupleDepGenM $ fileComponents name (base, ext) <- untupleDepGenM $ fileComponents name
thumbnailName <- concatStrings [ pure base, inject "-thumbnail.", pure ext ] thumbnailName <- concatStrings [ pure base, inject "-thumbnail.", pure ext ]
convertImage path (joinPaths recipeDirOut thumbnailName) (inject (ResizeToWidth 800)) imageResized <- convertImage (openImage path) (inject (ResizeToWidth 800))
saveImage imageResized (joinPaths recipeDirOut thumbnailName)
concatTexts [ inject "<p class=\"image\"><a href=\"" concatTexts [ inject "<p class=\"image\"><a href=\""
, toText name , toText name
, inject "\"><img src=\"" , inject "\"><img src=\""

View File

@ -15,5 +15,6 @@ data Function = AppendStrings
| MakeTemplate | MakeTemplate
| ApplyTemplate | ApplyTemplate
| ToText | ToText
| ConvertImage
deriving (Show, Lift) deriving (Show, Lift)

View File

@ -7,7 +7,8 @@ import Language.Haskell.TH.Syntax (Lift)
data FunctionIO = ListDirectory data FunctionIO = ListDirectory
| IsDirectory | IsDirectory
| ReadTextFile | ReadTextFile
| ConvertImage | OpenImage
| SaveImage
| SaveTextFile | SaveTextFile
| CopyFile | CopyFile
| MakeDir | MakeDir

View File

@ -1,22 +1,31 @@
module Types.Value module Types.Value
( Value(..) ( Value(..)
, Valuable(..) , Valuable(..)
, makeString
, makeImage
) where ) where
import Prelude hiding (String) import Prelude hiding (String)
import qualified Prelude
import Types.Values import Types.Values
import Data.Text (Text) import Data.Text (Text)
import qualified Codec.Picture as CP
import Language.Haskell.TH.Syntax (Lift) import Language.Haskell.TH.Syntax (Lift)
-- Note: We use a wrapper for the String type in order to be able to define the -- Note: We use a wrapper for the String type in order to be able to define the
-- general Valuable [a] instance further down. Otherwise it would conflict with -- general Valuable [a] instance further down. Otherwise it would conflict with
-- our Valuable String instance, since the non-wrapped String type is just an -- our Valuable String instance, since the non-wrapped String type is just an
-- alias for [Char]. -- alias for [Char].
--
-- Also note that the Image type does not actually implement Lift, so you
-- shouldn't use it with Inject. The better approach would be to split the
-- Value type into a compile-time version and a runtime version.
data Value = String String data Value = String String
| Text Text | Text Text
| Bool Bool | Bool Bool
| Image Image
| ImageConversionSettings ImageConversionSettings | ImageConversionSettings ImageConversionSettings
| Template Template | Template Template
| Empty | Empty
@ -24,6 +33,12 @@ data Value = String String
| List [Value] | List [Value]
deriving (Eq, Show, Lift) deriving (Eq, Show, Lift)
makeString :: Prelude.String -> Value
makeString = String . StringWrapper
makeImage :: CP.Image CP.PixelRGB8 -> Value
makeImage = Image . ImageWrapper
class Valuable a where class Valuable a where
toValue :: a -> Value toValue :: a -> Value
fromValue :: Value -> a fromValue :: Value -> a

View File

@ -1,6 +1,7 @@
module Types.Values module Types.Values
( String(..) ( String(..)
, FilePath , FilePath
, Image(..)
, ImageConversionSettings(..) , ImageConversionSettings(..)
, Template(..) , Template(..)
) where ) where
@ -10,7 +11,8 @@ import qualified Prelude
import Data.String (IsString(..)) import Data.String (IsString(..))
import Data.Text (Text) import Data.Text (Text)
import Language.Haskell.TH.Syntax (Lift) import qualified Codec.Picture as CP
import Language.Haskell.TH.Syntax (Lift(..))
newtype String = StringWrapper Prelude.String newtype String = StringWrapper Prelude.String
deriving (Eq, Show, Lift) deriving (Eq, Show, Lift)
@ -20,6 +22,15 @@ type FilePath = String
instance IsString String where instance IsString String where
fromString = StringWrapper fromString = StringWrapper
newtype Image = ImageWrapper (CP.Image CP.PixelRGB8)
deriving (Eq)
instance Show Image where
show = const "Image"
instance Lift Image where
liftTyped _ = error "cannot lift images"
data ImageConversionSettings = ResizeToWidth Int data ImageConversionSettings = ResizeToWidth Int
deriving (Eq, Show, Lift) deriving (Eq, Show, Lift)