packages feed

audiovisual (empty) → 0.0

raw patch · 15 files changed

+990/−0 lines, 15 filesdep +JuicyPixelsdep +JuicyPixels-utildep +WAVEsetup-changed

Dependencies added: JuicyPixels, JuicyPixels-util, WAVE, base, boundingboxes, colors, deepseq, directory, filepath, free, freetype2, hashable, lens, linear, mtl, objective, random, template-haskell, transformers, vector, void

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2014, Fumiaki Kinoshita
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * 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.
+
+    * Neither the name of Fumiaki Kinoshita nor the names of other
+      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
+OWNER 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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple
+main = defaultMain
+ audiovisual.cabal view
@@ -0,0 +1,62 @@+name:                audiovisual+version:             0.0+synopsis:            A battery-included audiovisual framework+homepage:            https://github.com/fumieval/audiovisual+bug-reports:         http://github.com/fumieval/audiovisual/issues+description:         Types and utilities for audio and graphic stuff+license:             BSD3+license-file:        LICENSE+author:              Fumiaki Kinoshita+maintainer:          Fumiaki Kinoshita <fumiexcel@gmail.com>+copyright:           Copyright (C) 2015 Fumiaki Kinoshita+category:            Data, Records+build-type:          Simple+stability:           provisional++extra-source-files: data/*.ttf+data-files: data/*.ttf+cabal-version:       >=1.10++source-repository head+  type: git+  location: https://github.com/fumieval/extensible.git++library+  exposed-modules:+    Data.Audio+    Data.Graphics+    Data.Graphics.Bitmap+    Data.Graphics.Sight+    Data.Graphics.Class+    Data.Graphics.Font+    Data.Graphics.Vertex+    Data.Input.Event+    Audiovisual.Sampler+    Audiovisual.Deck+    Audiovisual.Text+  other-modules:+    Paths_audiovisual+  build-depends:       base >= 4.7 && <5+    , template-haskell+    , linear+    , objective >= 1 && <1.1+    , free >= 4.8 && <5+    , colors+    , deepseq+    , mtl+    , filepath+    , directory+    , freetype2+    , hashable+    , random+    , boundingboxes+    , JuicyPixels-util+    , JuicyPixels+    , vector+    , lens+    , void+    , transformers+    , WAVE+  hs-source-dirs:      src+  ghc-options: -Wall+  default-language:    Haskell2010
+ data/Yasashisa-Gothic.ttf view

file too large to diff

+ src/Audiovisual/Deck.hs view
@@ -0,0 +1,54 @@+{-# LANGUAGE Rank2Types #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE ViewPatterns #-}
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE ConstraintKinds #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Call.Util.Deck
+-- Copyright   :  (c) Fumiaki Kinoshita 2014
+-- License     :  BSD3
+--
+-- Maintainer  :  Fumiaki Kinoshita <fumiexcel@gmail.com>
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-- The deck for a single stream
+--
+-----------------------------------------------------------------------------
+module Audiovisual.Deck (Deck(..), empty, source, pos, playing, playback) where
+import Control.Lens
+import Control.Monad.State.Strict
+import Data.Audio
+import Foreign.Storable
+import qualified Data.Vector.Storable as V
+
+data Deck a = Deck
+  { _src :: Source a
+  , _pos :: !Time
+  , _playing :: !Bool }
+
+empty :: Num a => Deck a
+empty = Deck (Source $ const 0) 0 False
+
+source :: Lens' (Deck a) (Source a)
+source f s = f (_src s) <&> \a -> s { _src = a }
+
+pos :: Lens' (Deck a) Time
+pos f s = f (_pos s) <&> \a -> s { _pos = a }
+
+playing :: Lens' (Deck a) Bool
+playing f s = f (_playing s) <&> \a -> s { _playing = a }
+
+playback :: (Num a, Storable a, MonadState (Deck a) m) => Time -> Int -> m (V.Vector a)
+playback dt n = do
+  Source s <- use source
+  pl <- use playing
+  t0 <- use pos
+  if pl
+    then do
+      pos += dt
+      return $ V.fromList $ take n $ map s [t0,t0 + dt / fromIntegral n..]
+    else return $ V.replicate n 0
+ src/Audiovisual/Sampler.hs view
@@ -0,0 +1,48 @@+{-# LANGUAGE FlexibleContexts, ConstraintKinds #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Call.Util.Deck
+-- Copyright   :  (c) Fumiaki Kinoshita 2014
+-- License     :  BSD3
+--
+-- Maintainer  :  Fumiaki Kinoshita <fumiexcel@gmail.com>
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-- Polyphonic sampler
+--
+-----------------------------------------------------------------------------
+module Audiovisual.Sampler where
+import qualified Data.Vector.Storable as V
+import qualified Data.Vector.Storable.Mutable as MV
+import Control.Monad.ST
+import Control.Monad.State.Strict
+import Data.Audio
+import Foreign.Storable
+
+data Sampler a = Sampler [(Sample a, Time)]
+
+empty :: Sampler a
+empty = Sampler []
+
+playback :: (Num a, Storable a, MonadState (Sampler a) m) => Time -> Int -> m (V.Vector a)
+playback dt n = do
+  Sampler vs <- get
+  let (vs'', r) = runST $ do
+        v <- MV.new n
+        vs' <- forM vs $ \(s0@(Sample d (Source s)), t0) -> do
+          if d > t0 then return []
+            else do
+              forM_ [0..n-1] $ \i -> do
+                z <- MV.unsafeRead v i
+                MV.unsafeWrite v i $ z + s (t0 + f * fromIntegral i)
+              return [(s0, t0 + dt)]
+        v' <- V.unsafeFreeze v
+        return (vs', v')
+  put $ Sampler $ concat vs''
+  return r
+  where
+    f = dt / fromIntegral n
+
+play :: MonadState (Sampler a) m => Sample a -> m ()
+play s = modify $ \(Sampler xs) -> Sampler $ (s, 0) : xs
+ src/Audiovisual/Text.hs view
@@ -0,0 +1,55 @@+{-# LANGUAGE ConstraintKinds, FlexibleContexts, BangPatterns, DeriveFunctor #-}
+module Audiovisual.Text (renderer, Typewriting(..), typewriter, putStr, clear, simple) where
+import Prelude hiding (putStr)
+import Data.Graphics.Bitmap (Bitmap(..))
+import Data.Graphics.Font
+import Data.Graphics.Sight
+import Control.Lens hiding (simple)
+import Control.Monad.Free
+import Control.Monad.State.Class
+import Control.Monad.Trans
+import Control.Applicative
+import Control.Object
+import Data.Monoid
+import Linear
+import Control.DeepSeq
+
+data Typewriting a = Render (Picture -> a) | Type Char a deriving Functor
+
+renderer :: (MonadIO m, Applicative m) => Font -> Float -> Object (Request Char (Bitmap, V2 Float, V2 Float)) m
+renderer font size = flyweight (liftIO . renderChar font size)
+
+typewriter :: MonadIO m => Float -> (Char -> m (Bitmap, V2 Float, V2 Float)) -> Object (Free Typewriting) m
+typewriter l req = iterative $ stateful go (V2 0 0, mempty) where
+  go (Type '\3' cont) = do
+    put (V2 0 0, mempty)
+    return cont
+  go (Type '\r' cont) = return cont
+  go (Type '\n' cont) = do
+    _1 . _y += l
+    _1 . _x .= 0
+    return cont
+  go (Type ch cont) = do
+    (pos, pic) <- get
+    (!bmp@(Bitmap img _ _), !ofs, !adv) <- lift $ req ch
+    return $! rnf img
+    put (pos + adv, pic <> translate (pos + ofs) (bitmap bmp))
+    return cont
+  go (Render cont) = uses _2 cont
+
+putStr :: String -> Free Typewriting ()
+putStr [] = return ()
+putStr (c:cs) = wrap (Type c (putStr cs))
+
+clear :: Free Typewriting ()
+clear = liftF $ Type '\3' ()
+
+simple :: MonadIO m => Font -> Float -> m (String -> Picture)
+simple font size = liftIO $ do
+  r <- new $ renderer font size
+  t <- new $ typewriter (size * 1.2) ((r.-) . request)
+  return $ \s -> Picture $ applyVFX $ EmbedIO $ do
+    t .- putStr s
+    p <- t .- liftF (Render id)
+    t .- clear
+    return $! unPicture p
+ src/Data/Audio.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE BangPatterns, DeriveFunctor #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Call.Data.Wave
+-- Copyright   :  (c) Fumiaki Kinoshita 2014
+-- License     :  BSD3
+--
+-- Maintainer  :  Fumiaki Kinoshita <fumiexcel@gmail.com>
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-----------------------------------------------------------------------------
+module Data.Audio (
+  Source(..),
+  Sample(..),
+  Stereo,
+  Time,
+  readWAVE
+) where
+
+import Data.WAVE
+import Linear
+import Control.Monad.IO.Class
+import qualified Data.Vector.Unboxed as V
+import GHC.Float
+import Data.Monoid
+import Control.Applicative
+
+type Time = Float
+type Stereo = V2 Float
+
+newtype Source a = Source (Time -> a) deriving Functor
+
+instance Applicative Source where
+  pure a = Source (const a)
+  Source f <*> Source g = Source (f <*> g)
+
+instance Num a => Monoid (Source a) where
+  mempty = pure 0
+  mappend = liftA2 (+)
+
+readWAVE :: MonadIO m => FilePath -> m (Sample Stereo)
+readWAVE path = liftIO $ do
+  WAVE h ss <- getWAVEFile path
+  let vec = V.fromList (map fr ss)
+      rate = fromIntegral (waveFrameRate h)
+      !dur = fromIntegral (V.length vec) / rate
+      sample t
+        | t < 0 || t >= dur - (1/rate) = zero
+        | otherwise = vec V.! round (t * rate)
+  return $ Sample dur (Source sample)
+  where
+    fr [a, b] = V2 (double2Float $ sampleToDouble a) (double2Float $ sampleToDouble b)
+    fr _ = zero
+
+data Sample a = Sample { sampleLength :: Time ,sampleSource :: Source a}
+
+-- TODO: Lazy processing
+ src/Data/Graphics.hs view
@@ -0,0 +1,12 @@+module Data.Graphics (
+  module Data.Graphics.Bitmap
+  , module Data.Graphics.Class
+  , module Data.Graphics.Sight
+  , module Data.Graphics.Font
+  , module Data.Graphics.Vertex) where
+import Data.Graphics.Bitmap (Bitmap(..))
+import Data.Graphics.Class
+import Data.Graphics.Sight
+import Data.Graphics.Font
+import Data.Graphics.Vertex
+
+ src/Data/Graphics/Bitmap.hs view
@@ -0,0 +1,150 @@+{-# LANGUAGE TemplateHaskell #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Call.Data.Bitmap
+-- Copyright   :  (c) Fumiaki Kinoshita 2014
+-- License     :  BSD3
+--
+-- Maintainer  :  Fumiaki Kinoshita <fumiexcel@gmail.com>
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-----------------------------------------------------------------------------
+module Data.Graphics.Bitmap (
+  Bitmap(Blank, Bitmap)
+  ,image
+  ,offset
+  ,hash
+  ,liftImage
+  ,liftImage'
+  ,size
+  ,clip
+  ,bbox
+  ,readFile
+  ,writeFile
+  ,loadBitmapsWith
+  ) where
+
+
+import Control.Applicative
+import Control.Lens
+import Control.Monad
+import Control.Monad.IO.Class
+import Control.Monad.ST
+import Data.Char
+import Data.Monoid
+import Language.Haskell.TH
+import Linear
+import Prelude hiding (readFile, writeFile)
+import qualified Codec.Picture as C
+import qualified Codec.Picture.RGBA8 as C
+import qualified Codec.Picture.Types as C
+import qualified Data.BoundingBox as B
+import qualified Data.Hashable as H
+import qualified Data.Vector.Storable as V
+import System.Directory
+import System.FilePath
+import System.IO.Unsafe
+import System.Random
+
+data Bitmap = Blank | Bitmap { _image :: !(C.Image C.PixelRGBA8), _offset :: !(V2 Int), _hash :: !Int }
+
+image :: Traversal' Bitmap (C.Image C.PixelRGBA8)
+image _ Blank = pure Blank
+image f (Bitmap i o h) = f i <&> \i' -> Bitmap i' o h
+
+offset :: Traversal' Bitmap (V2 Int)
+offset _ Blank = pure Blank
+offset f (Bitmap i o h) = f o <&> \o' -> Bitmap i o' h
+
+hash :: Traversal' Bitmap Int
+hash _ Blank = pure Blank
+hash f (Bitmap i o h) = f h <&> \h' -> Bitmap i o h'
+
+-- | `mappend` stitches the right operand to the left
+instance Monoid Bitmap where
+  mempty = Blank
+  mappend base@(Bitmap b (V2 x0 y0) h0) lay@(Bitmap l (V2 x1 y1) h1) = runST $ do
+    let box = B.union (bbox base) (bbox lay)
+    let V2 w h = box ^. B.size 0
+    img <- C.createMutableImage w h (C.PixelRGBA8 0 0 0 0) >>= C.unsafeFreezeImage
+    let ox = max 0 (x0 - x1)
+    let oy = max 0 (y0 - y1)
+    return $ Bitmap
+      (C.patchImage (C.patchImage img (ox, oy) b) (max 0 (x1 - x0), max 0 (y1 - y0)) l)
+      (V2 (x0 + ox) (y0 + oy))
+      (H.hash (h0, h1))
+  mappend Blank b = b
+  mappend b Blank = b
+
+clip :: Bitmap -> B.Box V2 Int -> Bitmap
+clip (Bitmap b (V2 ox oy) k) (B.Box (V2 x0 y0) (V2 x1 y1)) = Bitmap
+  (C.trimImage b (x1 - x0, y1 - y0) (x0 - ox, y0 - oy))
+  (V2 ox oy)
+  (H.hash (x0, y0, x1, y1, k))
+
+clip Blank _ = Blank
+
+bbox :: Bitmap -> B.Box V2 Int
+bbox (Bitmap (C.Image w h _) (V2 x y) _) = B.Box (V2 x y) (V2 (x+w) (y+h))
+bbox Blank = B.Box zero zero
+
+size :: Bitmap -> V2 Int
+size (Bitmap (C.Image w h _) _ _) = V2 w h
+size Blank = zero
+
+liftImage :: C.Image C.PixelRGBA8 -> Bitmap
+liftImage b@(C.Image _ _ r) = Bitmap b zero (V.foldl H.hashWithSalt 0 r)
+
+liftImage' :: MonadIO m => C.Image C.PixelRGBA8 -> m Bitmap
+liftImage' b = liftIO $ Bitmap b zero <$> randomIO
+
+-- | Load an image file.
+readFile :: MonadIO m => FilePath -> m Bitmap
+readFile path = liftIO $ Bitmap <$> C.readImageRGBA8 path <*> pure zero <*> randomIO
+
+-- | Save 'Bitmap' into a file.
+writeFile :: MonadIO m => FilePath -> Bitmap -> m ()
+writeFile path (Bitmap p _ _) = liftIO $ C.writePng path p
+writeFile _ Blank = fail "Blank bitmap"
+
+-- | The type of the given 'ExpQ' must be @FilePath -> IO FilePath@
+-- FIXME: This may cause name duplication if there are multiple non-alphanumeric file names.
+loadBitmapsWith :: ExpQ -> FilePath -> Q [Dec]
+loadBitmapsWith getFullPath path = do
+    loc <- (</>path) <$> takeDirectory <$> loc_filename <$> location
+    paths <- runIO $ getFileList loc
+
+    sequence $ do
+        p <- paths
+        let name = pathToName p
+        [ return $ SigD (mkName name) (ConT ''Bitmap)
+            , funD (mkName name) [clause [] (normalB $ load name $ loc </> p) []]
+            ]
+    where
+        load name fp = do
+            runIO $ putStrLn $ "Defined: " ++ fp ++ " as `" ++ name ++ "'"
+
+            appE (varE 'unsafePerformIO) $ uInfixE (appE getFullPath $ litE $ StringL fp)
+                (varE '(>>=))
+                (varE 'readFile)
+
+-- | Load and define all pictures in the specified directory.
+-- On base >= 4.6, file paths to actually load will be respect to the directory of the executable. Otherwise it will be based on the current directory.
+
+
+getFileList :: FilePath -> IO [FilePath]
+getFileList path = do
+    allContents <- filter notHidden `fmap` getDirectoryContents path
+
+    files <- filterM (doesFileExist . (path</>)) allContents
+    dirs <- filterM (doesDirectoryExist . (path</>)) allContents
+    fmap ((files++).concat) $ forM dirs $ \i -> map (i</>) `fmap` getFileList (path</>i)
+    where
+        notHidden ('.':_) = False
+        notHidden _ = True
+
+pathToName :: FilePath -> String
+pathToName = ('_':) . map p where
+    p c | isAlphaNum c = c
+        | otherwise = '_'
+ src/Data/Graphics/Class.hs view
@@ -0,0 +1,32 @@+{-# LANGUAGE TypeFamilies, DeriveDataTypeable #-}+module Data.Graphics.Class where+import Data.Typeable+import Linear++data PrimitiveMode = LineStrip | TriangleFan | TriangleStrip | LineLoop deriving (Enum, Eq, Ord, Read, Show, Typeable)++class Affine a where+  type Vec a :: *+  type Normal a :: *+  rotateOn :: Normal a -> a -> a+  scale :: Vec a -> a -> a+  translate :: Vec a -> a -> a++class Affine a => Figure a where+  primitive :: PrimitiveMode -> [Vec a] -> a+  color :: V4 Float -> a -> a+  line :: [Vec a] -> a+  polygon :: [Vec a] -> a+  polygonOutline :: [Vec a] -> a+  circle :: Normal a -> a+  circleOutline :: Normal a -> a++opacity :: Figure a => Float -> a -> a+opacity p = color (V4 1 1 1 p)++data BlendMode = Normal+    | Inverse+    | Add+    | Multiply+    | Screen+    deriving (Enum, Eq, Ord, Read, Show, Typeable)
+ src/Data/Graphics/Font.hs view
@@ -0,0 +1,120 @@+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Call.Data.Font
+-- Copyright   :  (c) Fumiaki Kinoshita 2014
+-- License     :  BSD3
+--
+-- Maintainer  :  Fumiaki Kinoshita <fumiexcel@gmail.com>
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-- Font rendering
+--
+-----------------------------------------------------------------------------
+module Data.Graphics.Font
+  ( Font
+  , readFont
+  , defaultFont
+  , fontBoundingBox
+  , metricsAscent
+  , metricsDescent
+  , renderChar
+  ) where
+
+import Control.Applicative
+import Control.Monad.IO.Class
+import Control.Monad
+import Data.BoundingBox
+import qualified Data.Vector.Storable as V
+import Linear
+import Data.Graphics.Bitmap
+import Graphics.Rendering.FreeType.Internal
+import qualified Graphics.Rendering.FreeType.Internal.GlyphSlot as GS
+import qualified Graphics.Rendering.FreeType.Internal.Vector as V
+import Graphics.Rendering.FreeType.Internal.Bitmap as B
+import Graphics.Rendering.FreeType.Internal.PrimitiveTypes as PT
+import Graphics.Rendering.FreeType.Internal.Face as F
+import Graphics.Rendering.FreeType.Internal.Library as L
+import Graphics.Rendering.FreeType.Internal.BBox as BB
+import Foreign.Marshal.Alloc
+import Foreign.C.Types
+import Foreign.C.String
+import Foreign.Storable
+import Foreign.ForeignPtr
+import Foreign.Ptr
+import System.IO.Unsafe
+import Codec.Picture
+import Codec.Picture.RGBA8
+import Paths_audiovisual
+
+-- | Font object
+data Font = Font FT_Face (Float, Float) (Box V2 Float)
+
+defaultFont :: Font
+defaultFont = unsafePerformIO $ do
+  path <- getDataFileName "data/Yasashisa-Gothic.ttf"
+  readFont path
+
+-- | Create a 'Font' from the given file.
+readFont :: MonadIO m => FilePath -> m Font
+readFont path = liftIO $ alloca $ \p -> do
+    runFreeType $ withCString path $ \str -> ft_New_Face freeType str 0 p
+    f <- peek p
+    b <- peek (F.bbox f)
+    asc <- peek (ascender f)
+    desc <- peek (descender f)
+    u <- fromIntegral <$> peek (units_per_EM f)
+    let box = fmap ((/u).fromIntegral) $ Box
+            (V2 (xMin b) (yMin b))
+            (V2 (xMax b) (yMax b))
+    return $ Font f (fromIntegral asc/u, fromIntegral desc/u) box
+
+-- | Get the font's metrics.
+metricsAscent :: Font -> Float
+metricsAscent (Font _ (a, _) _) = a
+
+-- | Get the font's metrics.
+metricsDescent :: Font -> Float
+metricsDescent (Font _ (_, d) _) = d
+
+-- | Get the font's bounding box.
+fontBoundingBox :: Font -> Box V2 Float
+fontBoundingBox (Font _ _ b) = b
+
+runFreeType :: IO CInt -> IO ()
+runFreeType m = do
+    r <- m
+    unless (r == 0) $ fail $ "FreeType Error:" Prelude.++ show r
+
+freeType :: FT_Library
+freeType = unsafePerformIO $ alloca $ \p -> do
+  runFreeType $ ft_Init_FreeType p
+  peek p
+
+renderChar :: Font -> Float -> Char -> IO (Bitmap, V2 Float, V2 Float)
+renderChar (Font face _ _) pixel ch = do
+  let dpi = 300
+
+  runFreeType $ ft_Set_Char_Size face 0 (floor $ pixel * 72 / fromIntegral dpi * 64) dpi dpi
+
+  ix <- ft_Get_Char_Index face (fromIntegral $ fromEnum ch)
+  runFreeType $ ft_Load_Glyph face ix ft_LOAD_DEFAULT
+
+  slot <- peek $ glyph face
+  runFreeType $ ft_Render_Glyph slot ft_RENDER_MODE_NORMAL
+
+  bmp <- peek $ GS.bitmap slot
+  left <- fmap fromIntegral $ peek $ GS.bitmap_left slot
+  top <- fmap fromIntegral $ peek $ GS.bitmap_top slot
+
+  let h = fromIntegral $ B.rows bmp
+      w = fromIntegral $ B.width bmp
+
+  fptr <- newForeignPtr_ $ castPtr $ buffer bmp
+
+  adv <- peek $ GS.advance slot
+  b <- liftImage' $ fromColorAndOpacity (PixelRGB8 255 255 255)
+        $ Image w h $ V.unsafeFromForeignPtr0 fptr $ h * w
+  return (b
+    , V2 (left + fromIntegral w / 2) (-top + fromIntegral h / 2)
+    , V2 (fromIntegral (V.x adv) / 64) 0)
+ src/Data/Graphics/Sight.hs view
@@ -0,0 +1,164 @@+{-# LANGUAGE TypeFamilies+  , FlexibleContexts+  , BangPatterns+  , DeriveFunctor+  , Rank2Types+  , FlexibleInstances+  , UndecidableInstances+  , ViewPatterns+  , ScopedTypeVariables+  , GeneralizedNewtypeDeriving #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Call.Scene+-- Copyright   :  (c) Fumiaki Kinoshita 2014+-- License     :  BSD3+--+-- Maintainer  :  Fumiaki Kinoshita <fumiexcel@gmail.com>+-- Stability   :  experimental+-- Portability :  non-portable+--+-----------------------------------------------------------------------------+module Data.Graphics.Sight (Affine(..)+    , Figure(..)+    , Picture(..)+    , opacity+    , bitmap+    , toward+    , Scene(..)+    , transformScene+    , vertices+    , Sight(..)+    , viewPicture+    , viewScene+    , fromPerspective+    , VFX(..)+    , applyVFX+    ) where+import qualified Data.Graphics.Bitmap as B+import qualified Data.BoundingBox as X+import Data.Monoid+import Linear+import qualified Data.Vector.Storable as V+import Control.Lens+import Data.Color+import Data.Graphics.Class+import Data.Graphics.Vertex++bitmap :: B.Bitmap -> Picture+bitmap bmp = Picture $ Scene+  $ \_ _ f _ _ -> f bmp TriangleStrip+    (V.fromList [V3 (-w/2) (-h/2) 0 `positionUV` V2 0 0+        , V3 (w/2) (-h/2) 0 `positionUV` V2 1 0+        , V3 (-w/2) (h/2) 0 `positionUV` V2 0 1+        , V3 (w/2) (h/2) 0 `positionUV` V2 1 1]) where+  V2 w h = fmap fromIntegral $ B.size bmp++toward :: Vec3 -> Picture -> Scene+toward n@((^/norm n) -> V3 x y z) (Picture s) = transformScene+  (m33_to_m44 $ fromQuaternion $ axisAngle (V3 (-y) x 0) $ acos $ z / norm n)+  s++newtype Scene = Scene { unScene :: forall r.+  r+  -> (r -> r -> r)+  -> (B.Bitmap -> PrimitiveMode -> V.Vector Vertex -> r)+  -> (VFX r -> r)+  -> (M44 Float -> r -> r)+  -> r+  }++data VFX r = SphericalAdd B.Bitmap r+  | SphericalMultiply B.Bitmap r+  | Diffuse (V4 Float) r+--  | Specular (V3 Float) r+--  | Ambient (V3 Float) r+  | NormalMap B.Bitmap r+  | EmbedIO (IO r)+  deriving Functor++instance Affine Scene where+  type Vec Scene = V3 Float+  type Normal Scene = V3 Float+  rotateOn v = transformScene $ m33_to_m44 $ fromQuaternion $ axisAngle v (norm v)+  scale (V3 x y z) = transformScene $ V4+    (V4 x 0 0 0)+    (V4 0 y 0 0)+    (V4 0 0 z 0)+    (V4 0 0 0 1)+  translate v = transformScene $ translation .~ v $ eye4++instance Figure Scene where+  primitive m vs = Scene $ \_ _ f _ _ -> f B.Blank m (V.fromList $ map positionOnly vs)+  color col (Scene s) = Scene $ \e a f b t -> b (Diffuse col (s e a f b t))+  line = primitive LineStrip+  polygon = primitive TriangleFan+  polygonOutline = primitive LineLoop+  circle v = toward v $ circle $ norm v+  circleOutline v = toward v $ circleOutline $ norm v++unit_circle :: Int -> [V2 Float]+unit_circle n = map angle [0,2*pi/fromIntegral n..2*pi]++instance Monoid Scene where+  mempty = Scene $ \e _ _ _ _ -> e+  mappend (Scene x) (Scene y) = Scene $ \e a f b t -> a (x e a f b t) (y e a f b t)++v2ToV3 :: Num a => V2 a -> V3 a+v2ToV3 (V2 x y) = V3 x y 0++vertices :: B.Bitmap -> PrimitiveMode -> V.Vector Vertex -> Scene+vertices b m v = Scene $ \_ _ f _ _ -> f b m v++transformScene :: M44 Float -> Scene -> Scene+transformScene m (Scene pic) = Scene $ \e a f b t -> t m (pic e a f b t)++applyVFX :: VFX Scene -> Scene+applyVFX vf = Scene $ \e a f b t -> b $ fmap (\(Scene s) -> s e a f b t) vf++newtype Picture = Picture { unPicture :: Scene } deriving Monoid++instance Affine Picture where+  type Vec Picture = V2 Float+  type Normal Picture = Float+  rotateOn t (Picture s) = Picture (transformScene m s) where+    m = V4 (V4 (cos t) (-sin t) 0 0) (V4 (sin t) (cos t) 0 0) (V4 0 0 1 0) (V4 0 0 0 1)+  translate (V2 x y) (Picture s) = Picture $ transformScene (translation .~ V3 x y 0 $ eye4) s+  scale (V2 x y) (Picture s) = Picture (transformScene m s) where+    m = V4 (V4 x 0 0 0) (V4 0 y 0 0) (V4 0 0 1 0) (V4 0 0 0 1)++instance Figure Picture where+  primitive m v = Picture $ primitive m $ map v2ToV3 v+  color col (Picture s) = Picture (color col s)+  line = primitive LineStrip+  polygon = primitive TriangleFan+  polygonOutline = primitive LineLoop+  circle r = polygon $ map (^*r) $ unit_circle 33+  circleOutline r = polygonOutline $ map (^*r) $ unit_circle 33++newtype Sight = Sight { unSight+  :: forall r.+  X.Box V2 Float+  -> r+  -> (r -> r -> r)+  -> (X.Box V2 Float -> M44 Float -> Bool -> Scene -> r)+  -> r+  }++instance Monoid Sight where+  mempty = Sight $ \_ e _ _ -> e+  mappend (Sight x) (Sight y) = Sight $ \b e a f -> a (x b e a f) (y b e a f)++viewPicture :: Picture -> Sight+viewPicture (Picture s) = Sight $ \box@(X.Box (V2 x0 y0) (V2 x1 y1)) _ _ f -> f box (ortho x0 x1 y1 y0 (-1) 1) False s++viewScene :: Float -- ^ FOV+  -> Float -- ^ Near plane+  -> Float -- ^ Far plane+  -> Scene -- ^ The scene+  -> Sight+viewScene fov near far = fromPerspective $ \box -> perspective fov (let V2 w h = box ^. X.size 0 in w/h) near far++fromPerspective :: (X.Box V2 Float -> M44 Float) -> Scene -> Sight+fromPerspective mat s = Sight $ \box _ _ f -> f box (mat box) True s+
+ src/Data/Graphics/Vertex.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE DeriveDataTypeable #-}+module Data.Graphics.Vertex where+import Foreign.Storable+import Foreign.Ptr+import Data.Typeable+import Control.Applicative+import Linear++type Vec2 = V2 Float+type Vec3 = V3 Float++data Vertex = Vertex { vPos :: {-# UNPACK #-} !Vec3+  , vUV :: {-# UNPACK #-} !Vec2+  , vNormal :: {-# UNPACK #-} !Vec3 }+  deriving (Show, Eq, Ord, Read, Typeable)++align1 :: Int+align1 = sizeOf (vPos undefined)++align2 :: Int+align2 = align1 + sizeOf (vUV undefined)++instance Storable Vertex where+  sizeOf _ = sizeOf (undefined :: Vec3) + sizeOf (undefined :: Vec2) + sizeOf (undefined :: Vec3)+  alignment _ = 0+  peek ptr = Vertex+    <$> peek (castPtr ptr)+    <*> peek (castPtr $ ptr `plusPtr` align1)+    <*> peek (castPtr $ ptr `plusPtr` align2)+  poke ptr (Vertex v t n) = do+    poke (castPtr ptr) v+    poke (castPtr ptr `plusPtr` align1) t+    poke (castPtr ptr `plusPtr` align2) n++positionUV :: Vec3 -> Vec2 -> Vertex+positionUV v p = Vertex v p (V3 0 0 1)++positionOnly :: Vec3 -> Vertex+positionOnly v = Vertex v zero (V3 0 0 1)
+ src/Data/Input/Event.hs view
@@ -0,0 +1,164 @@+{-# LANGUAGE DeriveDataTypeable #-}+module Data.Input.Event where+import Data.Typeable+import Data.Char+import Linear++data Chatter a = Up a | Down a deriving (Show, Eq, Ord, Read, Typeable)++data MouseEvent = Button (Chatter Int)+  | Cursor (V2 Float)+  | Scroll (V2 Float)+  deriving (Show, Eq, Ord, Read, Typeable)++data Gamepad = Gamepad Int String deriving (Show, Eq, Ord, Read, Typeable)++data GamepadEvent = PadButton Gamepad (Chatter Int)+  | PadAxis Gamepad Int Float+  | PadConnection (Chatter Gamepad)+  deriving (Show, Eq, Ord, Read, Typeable)++data Key =+      KeyUnknown+    | KeySpace+    | KeyApostrophe+    | KeyComma+    | KeyMinus+    | KeyPeriod+    | KeySlash+    | Key0+    | Key1+    | Key2+    | Key3+    | Key4+    | Key5+    | Key6+    | Key7+    | Key8+    | Key9+    | KeySemicolon+    | KeyEqual+    | KeyA+    | KeyB+    | KeyC+    | KeyD+    | KeyE+    | KeyF+    | KeyG+    | KeyH+    | KeyI+    | KeyJ+    | KeyK+    | KeyL+    | KeyM+    | KeyN+    | KeyO+    | KeyP+    | KeyQ+    | KeyR+    | KeyS+    | KeyT+    | KeyU+    | KeyV+    | KeyW+    | KeyX+    | KeyY+    | KeyZ+    | KeyLeftBracket+    | KeyBackslash+    | KeyRightBracket+    | KeyGraveAccent+    | KeyWorld1+    | KeyWorld2+    | KeyEscape+    | KeyEnter+    | KeyTab+    | KeyBackspace+    | KeyInsert+    | KeyDelete+    | KeyRight+    | KeyLeft+    | KeyDown+    | KeyUp+    | KeyPageUp+    | KeyPageDown+    | KeyHome+    | KeyEnd+    | KeyCapsLock+    | KeyScrollLock+    | KeyNumLock+    | KeyPrintScreen+    | KeyPause+    | KeyF1+    | KeyF2+    | KeyF3+    | KeyF4+    | KeyF5+    | KeyF6+    | KeyF7+    | KeyF8+    | KeyF9+    | KeyF10+    | KeyF11+    | KeyF12+    | KeyF13+    | KeyF14+    | KeyF15+    | KeyF16+    | KeyF17+    | KeyF18+    | KeyF19+    | KeyF20+    | KeyF21+    | KeyF22+    | KeyF23+    | KeyF24+    | KeyF25+    | KeyPad0+    | KeyPad1+    | KeyPad2+    | KeyPad3+    | KeyPad4+    | KeyPad5+    | KeyPad6+    | KeyPad7+    | KeyPad8+    | KeyPad9+    | KeyPadDecimal+    | KeyPadDivide+    | KeyPadMultiply+    | KeyPadSubtract+    | KeyPadAdd+    | KeyPadEnter+    | KeyPadEqual+    | KeyLeftShift+    | KeyLeftControl+    | KeyLeftAlt+    | KeyLeftSuper+    | KeyRightShift+    | KeyRightControl+    | KeyRightAlt+    | KeyRightSuper+    | KeyMenu+    deriving (Enum, Eq, Ord, Read, Show, Typeable, Bounded)++charToKey :: Char -> Key+charToKey ch+    | isAlpha ch = toEnum $ fromEnum KeyA + fromEnum ch - fromEnum 'A'+    | isDigit ch = toEnum $ fromEnum Key0 + fromEnum ch - fromEnum '0'+charToKey '-' = KeyMinus+charToKey ',' = KeyComma+charToKey '.' = KeyPeriod+charToKey '/' = KeySlash+charToKey ' ' = KeySpace+charToKey '\'' = KeyApostrophe+charToKey '\\' = KeyBackslash+charToKey '=' = KeyEqual+charToKey ';' = KeySemicolon+charToKey '[' = KeyLeftBracket+charToKey ']' = KeyRightBracket+charToKey '`' = KeyGraveAccent+charToKey '\n' = KeyEnter+charToKey '\r' = KeyEnter+charToKey '\t' = KeyTab+charToKey _ = KeyUnknown