module Halbum.ImageStore (Buffer,getBufferSize, ImageVersion, vWidth, vHeight, vPath, Size, saveImage, deleteImage) where import Halbum.Buffer import Halbum.ImageInfo import qualified Graphics.GD as GD import Data.Char import Data.List import Control.Monad import Data.Ratio import System.Directory type Size = (Int,Int) type ImageVersion = (Size, FilePath) vWidth :: ImageVersion -> Int vWidth = fst . fst vHeight :: ImageVersion -> Int vHeight = snd . fst vPath :: ImageVersion -> FilePath vPath = snd saveImage :: FilePath -- ^ Image storage directory -> ImageID -- ^ image id (must be unique) -> Buffer -- ^ image data -> String -- ^ name -> [Size] -- ^ image sizes to save, in addition to the full image. -> IO (ImageVersion,[ImageVersion]) -- ^ full image size, full image path, -- resized image sizes and paths. saveImage basedir imgid buf name sizes = do let file = imageFileName imgid name createDirectoryIfMissing True basedir writeBufferFile (realPath basedir file) buf img <- withBufferPtr buf (\ (p,l) -> GD.loadJpegData l p) size <- GD.imageSize img resized <- mapM (saveResized basedir img file size) sizes return ((size, file), resized) imageFileName :: ImageID -> String -> String imageFileName imgid name = show imgid ++ "-" ++ name' where name' = filter okChar name okChar c = isAlpha c || isDigit c || c `elem` "!\"#$%&'()*+,-.@[]^_`:;<=>?{|}~" saveResized :: FilePath -> GD.Image -> String -> Size -> Size -> IO ImageVersion saveResized basedir img file sz maxSz = do let dir = sizedir maxSz path = mkPath [dir, file] newSz@(w,h) = calcSize sz maxSz createDirectoryIfMissing True (realPath basedir dir) img' <- GD.resizeImage img w h GD.saveJpeg img' (realPath basedir path) (-1) return (newSz, path) sizedir :: Size -> FilePath sizedir (w,h) = show w ++ "x" ++ show h -- | Find the best size to resize to, given the desired size. calcSize :: Size -- ^ current size -> Size -- ^ wanted size -> Size -- ^ largest size smaller than wanted size -- such that the aspect ratio is preserved. calcSize (inW,inH) (outW,outH) | inAspect > outAspect = (outW, round (fromIntegral outW / inAspect)) | inAspect < outAspect = (round (fromIntegral outH * inAspect), outH) | otherwise = (outW, outH) where inAspect = inW % inH outAspect = outW % outH -- | Delete the given image. deleteImage :: FilePath -> ImageInfo -> IO () deleteImage basedir info = do removeFile $ realPath basedir $ fullFile info removeFile $ realPath basedir $ thumbFile info removeFile $ realPath basedir $ viewFile info realPath :: FilePath -> String -> String realPath basedir p = mkPath [basedir, p] mkPath :: [String] -> FilePath mkPath = concat . intersperse "/"