packages feed

imagemagick-0.0.1: examples/text_effects.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
-- | Example taken from: http://members.shaw.ca/el.supremo/MagickWand/text_effects.htm
{- There's no equivalent convert command for this. It is a demo of MagickWand.
See this forum thread for the genesis of these effects
http://www.imagemagick.org/discourse-server/viewtopic.php?f=6&t=11586
and Anthony's Text Effects page at:
http://www.imagemagick.org/Usage/fonts/
-}

import           Control.Monad                         (when)
import           Control.Monad.IO.Class                (MonadIO)
import           Control.Monad.Trans.Resource
import           Data.ByteString                       (ByteString)
import           Data.Text                             (Text)
import qualified Data.Text                             as T
import           Filesystem.Path.CurrentOS
import           Graphics.ImageMagick.MagickCore.Types
import           Graphics.ImageMagick.MagickWand
import           Prelude                               hiding (FilePath)

-- see http://www.imagemagick.org/Usage/#font about using fonts with IM
font :: ByteString
font = "VerdanaBI"

-- Text effect 1 - shadow effect using MagickShadowImage
-- This is derived from Anthony's Soft Shadow effect
-- convert -size 300x100 xc:none -font Candice -pointsize 72 \
--           -fill white  -stroke black  -annotate +25+65 'Anthony' \
--           \( +clone -background navy  -shadow 70x4+5+5 \) +swap \
--           -background lightblue -flatten  -trim +repage  font_shadow_soft.jpg

-- NOTE - if an image has a transparent background, adding a border of any colour other
-- than "none" will remove all the transparency and replace it with the border's colour
textEffect1 :: (MonadResource m) => PMagickWand -> PDrawingWand -> PPixelWand -> m ()
textEffect1 w dw pw = do
  pw `setColor` "none"
  -- Create a new transparent image
  newImage w 350 100 pw
  -- Set up a 72 point white font
  pw `setColor` "white"
  dw `setFillColor` pw
  dw `setFont` font
  dw `setFontSize` 72
  -- Add a black outline to the text
  pw `setColor` "black"
  dw `setStrokeColor` pw
  -- Turn antialias on - not sure this makes a difference
  dw `setTextAntialias` True
  -- Now draw the text
  drawAnnotation dw 25 65 "Magick"
  -- Draw the image on to the magick_wand
  drawImage w dw

  -- Trim the image down to include only the text
  trimImage w 0
  -- equivalent to the command line +repage
  resetImagePage w Nothing

  -- Make a copy of the text image
  (_,cloneW) <- cloneMagickWand w
  -- Set the background colour to blue for the shadow
  pw `setColor` "blue"
  w `setImageBackgroundColor` pw

  -- Opacity is a real number indicating (apparently) percentage
  shadowImage w 70 4 5 5

  -- Composite the text on top of the shadow
  compositeImage w cloneW overCompositeOp 5 5

  (_,w') <- magickWand
  -- Create a new image the same size as the text image and put a solid colour
  -- as its background
  pw `setColor` "rgb(125,215,255)"
  width <- getImageWidth w
  height <- getImageHeight w
  newImage w' width height pw
  -- Now composite the shadowed text over the plain background
  compositeImage w' w overCompositeOp 0 0
  --  and write the result
  writeImage w' (Just "text_shadow.png")


-- Given a pattern name (which MUST have a leading #) and a pattern file,
-- set up a pattern URL for later reference in the specified drawing wand
-- Currently only used in Text Effect 2
setTilePattern :: (MonadResource m) => PDrawingWand -> Text -> FilePath -> m ()
setTilePattern dw patternName patternFile = do
  (_,w) <- magickWand
  readImage w patternFile
  -- Read the tile's width and height
  width <- getImageWidth w
  height <- getImageHeight w

  pushPattern dw (T.tail patternName) 0 0 (realToFrac width) (realToFrac height)
  drawComposite dw srcOverCompositeOp 0 0 0 0 w
  popPattern dw
  dw `setFillPatternURL` patternName


-- Text effect 2 - tiled text using the builtin checkerboard pattern
-- Anthony's Tiled Font effect
-- convert -size 320x100 xc:lightblue -font Candice -pointsize 72 \
--          -tile pattern:checkerboard   -annotate +28+68 'Anthony' \
--           font_tile.jpg
textEffect2 :: (MonadResource m) => PMagickWand -> PDrawingWand -> PPixelWand -> m ()
textEffect2 w dw pw = do
  setTilePattern dw "#check" "pattern:checkerboard"

  pw `setColor` "lightblue"
  -- Create a new transparent image
  newImage w 320 100 pw

  -- Set up a 72 point font
  dw `setFont` font
  dw `setFontSize` 72
  -- Now draw the text
  drawAnnotation dw 28 68 "Magick"
  -- Draw the image on to the magick_wand
  drawImage w dw
  -- Trim the image
  trimImage w 0
  -- Add a transparent border
  pw `setColor` "lightblue"
  borderImage w pw 5 5
  -- and write it
  writeImage w (Just "text_pattern.png")

-- Text effect 3 -  arc font (similar to http://www.imagemagick.org/Usage/fonts/#arc)
-- convert -size 320x100 xc:lightblue -font Candice -pointsize 72 \
--           -annotate +25+65 'Anthony' -distort Arc 120 \
--           -trim +repage -bordercolor lightblue -border 10  font_arc.jpg
textEffect3 :: (MonadResource m) => PMagickWand -> PDrawingWand -> PPixelWand -> m ()
textEffect3 w dw pw = do
  -- Create a 320x100 lightblue canvas
  pw `setColor` "lightblue"
  newImage w 320 100  pw

  -- Set up a 72 point font
  dw `setFont` font
  dw `setFontSize` 72
  -- Now draw the text
  drawAnnotation dw 25 65 "Magick"
  -- Draw the image on to the magick_wand
  drawImage w dw

  let dargs = [120]
  distortImage w arcDistortion dargs False
  -- Trim the image
  trimImage w 0
  -- Add the border
  pw `setColor` "lightblue"
  borderImage w pw 10 10

  -- and write it
  writeImage w (Just "text_arc.png")


-- Text effect 4 - bevelled font http://www.imagemagick.org/Usage/fonts/#bevel
-- convert -size 320x100 xc:black -font Candice -pointsize 72 \
--              -fill white   -annotate +25+65 'Anthony' \
--              -shade 140x60  font_beveled.jpg
textEffect4 :: (MonadResource m) => PMagickWand -> PDrawingWand -> PPixelWand -> m ()
textEffect4 w dw pw = do
  let colorize = False
  -- Create a 320x100 canvas
  pw `setColor` "gray"
  newImage w 320 100 pw
  -- Set up a 72 point font
  dw `setFont` font
  dw `setFontSize` 72
  -- Set up a 72 point white font
  pw `setColor` "white"
  dw `setFillColor` pw
  -- Now draw the text
  drawAnnotation dw 25 65 "Magick"
  -- Draw the image on to the magick_wand
  drawImage w dw
  -- the "gray" parameter must be true to get the effect shown on Anthony's page
  shadeImage w True 140 60

  when colorize $ do
    pw `setColor` "yellow"
    dw `setFillColor` pw
    pw' <- pixelWand
    pw' `setColor` "gold"
    colorizeImage w pw pw'

  -- and write it
  writeImage w (Just "text_bevel.png")


-- Text effect 5 and 6 - Plain text and then Barrel distortion
textEffects5_6 :: (MonadResource m) => PMagickWand -> PDrawingWand -> PPixelWand -> m ()
textEffects5_6 w dw pw = do
  -- Create a 320x100 transparent canvas
  pw `setColor` "none"
  newImage w 320 100 pw

  -- Set up a 72 point font
  dw `setFont` font
  dw `setFontSize` 72
  -- Now draw the text
  drawAnnotation dw 25 65 "Magick"
  -- Draw the image on to the magick_wand
  drawImage w dw
  writeImage w (Just"text_plain.png")

  -- Trim the image
  trimImage w 0
  -- Add the border
  pw `setColor` "none"
  borderImage w pw 10 10
  -- MagickSetImageMatte(magick_wand,MagickTrue);
  -- MagickSetImageVirtualPixelMethod(magick_wand,TransparentVirtualPixelMethod);
  -- d_args[0] = 0.1;d_args[1] = -0.25;d_args[2] = -0.25; [3] += .1
  -- The first value should be positive. If it is negative the image is *really* distorted
  -- d_args[0] = 0.0;
  -- d_args[1] = 0.0;
  -- d_args[2] = 0.5;
  -- d_args[3] should normally be chosen such the sum of all 4 values is 1
  -- so that the result is the same size as the original
  -- You can override the sum with a different value
  -- If the sum is greater than 1 the resulting image will be smaller than the original
  -- d_args[3] = 1 - (d_args[0] + d_args[1] + d_args[2]);
  -- Make the result image smaller so that it isn't as likely
  -- to overflow the edges
  -- d_args[3] += 0.1;
  -- 0.0,0.0,0.5,0.5,0.0,0.0,-0.5,1.9
  -- d_args[3] = 0.5;
  -- d_args[4] = 0.0;
  -- d_args[5] = 0.0;
  -- d_args[6] = -0.5;
  -- d_args[7] = 1.9;

  let d_args = [0, 0, 0.5, 1 - (0 + 0 + 0.5), 0, 0, -0.5, 1.9]
  -- DON'T FORGET to set the correct number of arguments here
  distortImage w barrelDistortion d_args True
  -- MagickResetImagePage(magick_wand,"");
  -- Trim the image again
  trimImage w 0
  -- Add the border
  pw `setColor` "none"
  borderImage w pw 10 10
  -- and write it
  writeImage w (Just "text_barrel.png")

-- Text effect 7 - Polar distortion
textEffect7 :: (MonadResource m) => PMagickWand -> PDrawingWand -> PPixelWand -> m ()
textEffect7 w dw pw = do
  -- Create a 320x200 transparent canvas
  pw `setColor` "none"
  newImage w 320 200 pw

  -- Set up a 72 point font
  dw `setFont` font
  dw `setFontSize` 72
  -- Now draw the text
  drawAnnotation dw 25 65 "Magick"
  -- Draw the image on to the magick_wand
  drawImage w dw

  distortImage w polarDistortion [0] True
  -- MagickResetImagePage(magick_wand,"");
  -- Trim the image again
  trimImage w 0
  -- Add the border
  pw `setColor` "none"
  borderImage w pw 10 10
  -- and write it
  writeImage w (Just "text_polar.png")

-- Text effect 8 - Shepard's distortion
textEffect8 :: (MonadResource m) => PMagickWand -> PDrawingWand -> PPixelWand -> m ()
textEffect8 w dw pw = do
  -- Create a 320x200 transparent canvas
  pw `setColor` "none"
  newImage w 640 480 pw

  -- Set up a 72 point font
  dw `setFont` font
  dw `setFontSize` 72
  -- Now draw the text
  drawAnnotation dw 50 240 "Magick Rocks"
  -- Draw the image on to the magick_wand
  drawImage w dw
  let d_args = [ 150.0, 190.0, 100.0, 290.0, 500.0, 200.0, 430.0, 130.0 ]
  distortImage w shepardsDistortion d_args True

  -- Trim the image
  trimImage w 0
  -- Add the border
  pw `setColor` "none"
  borderImage w pw 10 10
  -- and write it
  writeImage w (Just "text_shepards.png")

runEffect :: (MonadIO m, MonadUnsafeIO m, MonadThrow m, MonadBaseControl IO m) =>
             (PMagickWand -> PDrawingWand -> PPixelWand -> ResourceT m ()) -> m ()
runEffect e = localGenesis $ do
  (_,w) <- magickWand
  (_,dw) <- drawingWand
  pw <- pixelWand
  e w dw pw

main :: IO ()
main = withMagickWandGenesis $ do
  runEffect textEffect1
  runEffect textEffect2
  runEffect textEffect3
  runEffect textEffect4
  runEffect textEffects5_6
  runEffect textEffect7
  runEffect textEffect8