imagemagick-0.0.4: src/Graphics/ImageMagick/MagickWand/MagickWand.hs
{-# LANGUAGE FlexibleContexts #-}
module Graphics.ImageMagick.MagickWand.MagickWand
( withMagickWandGenesis
, localGenesis
-- * Creation
, magickWand
, wandResource
-- , magickWandFromImage
-- , isMagickWand
, cloneMagickWand
-- , clearMagickWand
, getSize
, setSize
, setImageArtifact
, deleteImageArtifact
, getIteratorIndex
, setIteratorIndex
-- , setFirstIterator
-- , setLastIterator
, resetIterator
, magickIterate
, magickIterateReverse
, deleteOption
, getOption
, setOption
, getOptions
, deleteImageProperty
, getImageProperty
, setImageProperty
, getImageProperties
, getImageProfile
, removeImageProfile
, setImageProfile
, getImageProfiles
-- , queryConfigureOption
-- , queryConfigureOptions
-- , queryFontMetrics
-- , queryMultilineFontMetrics
-- , queryFonts
-- , relinquishMemory
-- , deleteImageArtifact
-- , deleteImageProperty
-- , getAntialias
-- , getBackgroundColor
, getColorspace
, getCompression
, getCompressionQuality
-- , getCopyright
-- , getFilename
-- , getFont
-- , getFormat
-- , getGravity
-- , getHomeURL
-- , getImageArtifact
, getImageArtifacts
-- , getInterlaceScheme
-- , getInterpolateMethod
-- , getOrientation
-- , getPackageName
-- , getPage
-- , getPointsize
-- , getQuantumDepth
-- , getQuantumRange
-- , getReleaseDate
, getImageResolution
-- , getResource
-- , getResourceLimit
-- , getSamplingFactors
-- , getSize
-- , getSizeOffset
-- , getType
-- , getVersion
-- , profileImage
-- , removeImageProfile
-- , setAntialias
-- , setBackgroundColor
, setColorspace
, setCompression
, setCompressionQuality
-- , setDepth
-- , setExtract
-- , setFilename
-- , setFont
-- , setFormat
-- , setGravity
-- , setImageArtifact
-- , setImageProfile
-- , setInterlaceScheme
-- , setInterpolateMethod
-- , setOrientation
-- , setPage
-- , setPassphrase
-- , setPointsize
-- , setProgressMonitor
-- , setResourceLimit
, setImageResolution
-- , setSamplingFactors
-- , setSizeOffset
-- , setType
) where
import Control.Applicative ((<$>))
import Control.Exception
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.Trans.Resource
import Data.ByteString
import Data.Text (Text)
import Data.Text.Encoding (decodeUtf8,
encodeUtf8)
import Data.Vector.Storable (Vector)
import qualified Data.Vector.Storable as V
import Foreign hiding
(void)
import Foreign.C.String
import Foreign.C.Types
import qualified Graphics.ImageMagick.MagickWand.FFI.MagickWand as F
import Graphics.ImageMagick.MagickWand.FFI.Types
import qualified Graphics.ImageMagick.MagickWand.FFI.WandProperties as F
import Graphics.ImageMagick.MagickWand.Types
import Graphics.ImageMagick.MagickWand.Utils
-- | Create magic wand environment and closes it at the
-- end of the work, should wrap all MagickWand functions
-- withMagickWandGenesis :: IO a -> IO a
-- withMagickWandGenesis :: (MonadCatchIO m, MonadBaseControl IO m, MonadCatchIO (ResourceT IO)) => (ResourceT m c) -> m c
withMagickWandGenesis :: ResourceT IO c -> IO c
withMagickWandGenesis f = bracket start finish (\_ -> runResourceT f)
where
start = liftIO F.magickWandGenesis
finish = liftIO . const F.magickWandTerminus
-- | Open a nested block inside genesis (for tracking nested resources)
localGenesis :: MonadBaseControl IO m => ResourceT m a -> m a
localGenesis f = runResourceT f
magickWand :: (MonadResource m) => m (ReleaseKey, Ptr MagickWand)
magickWand = wandResource F.newMagickWand
magickIterateF :: (MonadResource m) =>
(PMagickWand -> IO ())
-> (PMagickWand -> IO MagickBooleanType)
-> PMagickWand -> (PMagickWand -> m ()) -> m ()
magickIterateF initF next w f = liftIO (initF w) >> go -- TODO: use fix
where
go = do
i <- liftIO $ next w
when (i==mTrue) $ f w >> go
magickIterate :: (MonadResource m) => Ptr MagickWand -> (Ptr MagickWand -> m ()) -> m ()
magickIterate = magickIterateF F.magickResetIterator F.magickNextImage
magickIterateReverse :: (MonadResource m) => Ptr MagickWand -> (Ptr MagickWand -> m ()) -> m ()
magickIterateReverse = magickIterateF F.magickSetLastIterator F.magickPreviousImage
wandResource :: (MonadResource m) => (IO (Ptr MagickWand)) -> m (ReleaseKey, Ptr MagickWand)
wandResource f = allocate f destroy
where destroy = void . F.destroyMagickWand
cloneMagickWand :: (MonadResource m) => Ptr MagickWand -> m (ReleaseKey, Ptr MagickWand)
cloneMagickWand = wandResource . F.cloneMagickWand
setSize :: (MonadResource m) => Ptr MagickWand -> Int -> Int -> m ()
setSize w cols rows = withException_ w $ F.magickSetSize w (fromIntegral cols) (fromIntegral rows)
-- | Returns the size associated with the magick wand.
getSize :: (MonadResource m) => Ptr MagickWand -> m (Int, Int)
getSize w = liftIO $ alloca $ \pw -> do
height <- alloca $ \ph -> F.magickGetSize w pw ph >> peek ph >>= return
width <- peek pw
return (fromIntegral width, fromIntegral height)
-- | MagickSetImageArtifact() associates a artifact with an image.
-- The format of the MagickSetImageArtifact method is:
setImageArtifact :: (MonadResource m) => PMagickWand -> ByteString -> ByteString -> m () -- TODO use normal types
setImageArtifact w a v = withException_ w $ useAsCString a
$ \a' -> useAsCString v
$ F.magickSetImageArtifact w a'
-- | MagickDeleteImageArtifact() deletes a wand artifact.
deleteImageArtifact :: (MonadResource m) => PMagickWand -> ByteString -> m ()
deleteImageArtifact w a = withException_ w $ useAsCString a
$ F.magickDeleteImageArtifact w
-- | Sets the iterator to the given position in the image list specified
-- with the index parameter. A zero index will set the first image as
-- current, and so on. Negative indexes can be used to specify an image
-- relative to the end of the images in the wand, with -1 being the last
-- image in the wand.
setIteratorIndex :: (MonadResource m) => Ptr MagickWand -> Int -> m ()
setIteratorIndex w i = withException_ w $ F.magickSetIteratorIndex w (fromIntegral i)
-- | Returns the position of the iterator in the image list.
getIteratorIndex :: (MonadResource m) => Ptr MagickWand -> m Int
getIteratorIndex w = liftIO $ fromIntegral <$> F.magickGetIteratorIndex w
resetIterator :: (MonadResource m) => Ptr MagickWand -> m ()
resetIterator = liftIO . F.magickResetIterator
getOption :: (MonadResource m) => Ptr MagickWand -> Text -> m Text
getOption w key = liftIO $ do
cstr <- useAsCString (encodeUtf8 key) (F.magickGetOption w)
value <- decodeUtf8 <$> packCString cstr
F.magickRelinquishMemory (castPtr cstr)
return value
-- | Associates one or options with the wand (e.g. setOption wand "jpeg:perserve" "yes").
deleteOption :: (MonadResource m) => Ptr MagickWand -> Text -> m ()
deleteOption w key =
withException_ w $ useAsCString (encodeUtf8 key) (F.magickDeleteOption w)
-- | Associates one or options with the wand (e.g. setOption wand "jpeg:perserve" "yes").
setOption :: (MonadResource m) => Ptr MagickWand -> Text -> Text -> m ()
setOption w key value =
withException_ w $ useAsCString (encodeUtf8 key) $
\cstr -> useAsCString (encodeUtf8 value) (F.magickSetOption w cstr)
getOptions :: (MonadResource m) => Ptr MagickWand -> Text -> m [Text]
getOptions w pattern = liftIO $ alloca $ \pn -> do
poptionps <- useAsCString (encodeUtf8 pattern) (\cstr -> F.magickGetOptions w cstr pn)
n <- fromIntegral <$> peek pn
optionps <- peekArray n poptionps
options <- forM optionps $ \optionp -> do
option <- decodeUtf8 <$> packCString optionp
F.magickRelinquishMemory (castPtr optionp)
return option
F.magickRelinquishMemory (castPtr poptionps)
return options
-- | Deletes a wand property
deleteImageProperty :: (MonadResource m) => Ptr MagickWand -> Text -> m ()
deleteImageProperty w prop =
withException_ w $ useAsCString (encodeUtf8 prop) (F.magickDeleteImageProperty w)
-- | Returns a value associated with the specified property
getImageProperty :: (MonadResource m) => Ptr MagickWand -> Text -> m Text
getImageProperty w prop = liftIO $ do
cstr <- useAsCString (encodeUtf8 prop) (F.magickGetImageProperty w)
value <- decodeUtf8 <$> packCString cstr
F.magickRelinquishMemory (castPtr cstr)
return value
-- | Associates a property with an image.
setImageProperty :: (MonadResource m) => Ptr MagickWand -> Text -> Text -> m ()
setImageProperty w prop value =
withException_ w $ useAsCString (encodeUtf8 prop) $
\cstr -> useAsCString (encodeUtf8 value) (F.magickSetImageProperty w cstr)
-- | Returns all the property names that match the specified pattern associated
-- with a wand
getImageProperties :: (MonadResource m) => Ptr MagickWand -> Text -> m [Text]
getImageProperties w pattern = liftIO $ alloca $ \pn -> do
ppropps <- useAsCString (encodeUtf8 pattern) (\cstr -> F.magickGetImageProperties w cstr pn)
n <- fromIntegral <$> peek pn
propps <- peekArray n ppropps
props <- forM propps $ \propp -> do
prop <- decodeUtf8 <$> packCString propp
F.magickRelinquishMemory (castPtr propp)
return prop
F.magickRelinquishMemory (castPtr ppropps)
return props
getProfile :: (MonadResource m) =>
(PMagickWand -> CString -> Ptr CSize -> IO (Ptr Word8)) ->
PMagickWand -> Text -> m (Vector Word8)
getProfile f w name = liftIO $ do
(pprofile, len) <- alloca $ \pn -> useAsCString (encodeUtf8 name) $ \cstr -> do
p <- f w cstr pn
n <- fromIntegral <$> peek pn
return (p,n)
-- TODO: maybe we should use copyBytes instead?
profile <- V.generateM len (peekElemOff pprofile)
F.magickRelinquishMemory (castPtr pprofile)
return profile
-- | Returns the named image profile.
getImageProfile :: (MonadResource m) => Ptr MagickWand -> Text -> m (Vector Word8)
getImageProfile = getProfile F.magickGetImageProfile
-- | Removes the named image profile and returns it
removeImageProfile :: (MonadResource m) => Ptr MagickWand -> Text -> m (Vector Word8)
removeImageProfile = getProfile F.magickRemoveImageProfile
-- | Adds a named profile to the magick wand. If a profile with the same
-- name already exists, it is replaced. This method differs from the
-- `profileImage` method in that it does not apply any CMS color profiles.
setImageProfile :: (MonadResource m) => Ptr MagickWand -> Text -> Vector Word8 -> m ()
setImageProfile w name profile =
withException_ w $ useAsCString (encodeUtf8 name) $
\cstr -> V.unsafeWith profile $
\p -> (F.magickSetImageProfile w cstr) p (fromIntegral $ V.length profile)
-- | Returns all the profile names that match the specified pattern
-- associated with a wand.
getImageProfiles :: (MonadResource m) => Ptr MagickWand -> Text -> m [Text]
getImageProfiles w pattern = liftIO $ alloca $ \pn -> do
pprofileps <- useAsCString (encodeUtf8 pattern) (\cstr -> F.magickGetImageProfiles w cstr pn)
n <- fromIntegral <$> peek pn
profileps <- peekArray n pprofileps
profiles <- forM profileps $ \profilep -> do
profile <- decodeUtf8 <$> packCString profilep
F.magickRelinquishMemory (castPtr profilep)
return profile
F.magickRelinquishMemory (castPtr pprofileps)
return profiles
-- | MagickGetColorspace() gets the wand colorspace type.
getColorspace :: (MonadResource m) => PMagickWand -> m ColorspaceType
getColorspace = liftIO . F.magickGetColorspace
-- | MagickSetColorspace() sets the wand colorspace type.
setColorspace :: (MonadResource m) => PMagickWand -> ColorspaceType -> m ()
setColorspace w c = withException_ w $ F.magickSetColorspace w c
getCompression :: (MonadResource m) => PMagickWand -> m CompressionType
getCompression = liftIO . F.magickGetCompression
setCompression :: (MonadResource m) => PMagickWand -> CompressionType -> m ()
setCompression w c = withException_ w $ F.magickSetCompression w c
getCompressionQuality :: (MonadResource m) => PMagickWand -> m Int
getCompressionQuality w = liftIO $ F.magickGetCompressionQuality w >>= return . fromIntegral
setCompressionQuality :: (MonadResource m) => PMagickWand -> Int -> m ()
setCompressionQuality w c = withException_ w $ F.magickSetCompressionQuality w (fromIntegral c)
getImageResolution :: (MonadResource m) => PMagickWand -> m (Double,Double)
getImageResolution w = liftIO $ alloca $ \py -> do
x <- alloca $ \px -> withExceptionIO w $ do
result <- F.magickGetImageResolution w px py
value <- peek px
return (result, value)
y <- peek py
return (realToFrac x, realToFrac y)
setImageResolution :: (MonadResource m) => PMagickWand -> Double -> Double -> m ()
setImageResolution w x y = withException_ w $ F.magickSetImageResolution w (realToFrac x) (realToFrac y)
getImageArtifacts :: (MonadResource m) => Ptr MagickWand -> Text -> m [Text]
getImageArtifacts w pattern = liftIO $ alloca $ \pn -> do
partifactps <- useAsCString (encodeUtf8 pattern) (\cstr -> F.magickGetImageArtifacts w cstr pn)
n <- fromIntegral <$> peek pn
artifactps <- peekArray n partifactps
artifacts <- forM artifactps $ \artifactp -> do
artifact <- decodeUtf8 <$> packCString artifactp
F.magickRelinquishMemory (castPtr artifactp)
return artifact
F.magickRelinquishMemory (castPtr partifactps)
return artifacts