typograffiti-0.1.0.0: src/Typograffiti/Store.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- |
-- Module: Typograffiti.Monad
-- Copyright: (c) 2018 Schell Scivally
-- License: MIT
-- Maintainer: Schell Scivally <schell@takt.com>
--
-- A storage context an ops for rendering text with multiple fonts
-- and sizes, hiding the details of the Atlas and WordCache.
module Typograffiti.Store where
import Control.Concurrent.STM (TMVar, atomically, newTMVar, putTMVar,
readTMVar, takeTMVar)
import Control.Monad.Except (MonadError (..), liftEither)
import Control.Monad.IO.Class (MonadIO (..))
import Data.Map (Map)
import qualified Data.Map as M
import Data.Set (Set)
import qualified Data.Set as S
import Linear
import Typograffiti.Atlas
import Typograffiti.Cache
import Typograffiti.Glyph
-- | A pre-rendered bit of text, ready to display given
-- some post compilition transformations. Also contains
-- the text size.
data RenderedText t m = RenderedText
{ drawRenderedText :: t -> m ()
, sizeOfRenderedText :: V2 Int
}
data Font t = Font
{ fontAtlas :: Atlas
, fontWordCache :: WordCache t
}
data TextRenderingData t = TextRenderingData
{ textRenderingDataAllocWord :: Atlas -> String -> IO (Either TypograffitiError (AllocatedRendering t))
-- ^ The operation used to alloc a word.
-- Generate geometry, use a shader program, set uniforms, etc.
, textRenderingDataFontMap :: Map (FilePath, GlyphSize) (Font t)
-- ^ The cached fonts.
, textRenderingDataCharSet :: Set Char
-- ^ The character set to have available in all allocated Atlas types.
}
-- | Stored fonts at specific sizes.
newtype FontStore t = FontStore
{ unFontStore :: TMVar (TextRenderingData t)}
getTextRendering
:: ( MonadIO m
, MonadError TypograffitiError m
, Layout t
)
=> FontStore t
-- ^ The font store.
-> FilePath
-- ^ The path to the font to use
-- for rendering.
-> GlyphSize
-- ^ The size of the font glyphs.
-> String
-- ^ The string to render.
-> m (RenderedText t m)
-- ^ The rendered text, ready to draw to the screen.
getTextRendering store file sz str = do
let mvar = unFontStore store
s <- liftIO $ atomically $ readTMVar mvar
font <- case M.lookup (file, sz) $ textRenderingDataFontMap s of
Nothing -> allocFont store file sz
Just font -> return font
(draw, tsz, cache) <-
loadText
(\x y -> liftIO (textRenderingDataAllocWord s x y) >>= liftEither)
(fontAtlas font)
(fontWordCache font)
str
liftIO
$ atomically $ do
s1 <- takeTMVar mvar
let alterf Nothing = Just $ Font (fontAtlas font) cache
alterf (Just (Font atlas _)) = Just $ Font atlas cache
fontmap = M.alter alterf (file,sz)
$ textRenderingDataFontMap s1
putTMVar mvar s1{ textRenderingDataFontMap = fontmap }
return RenderedText
{ drawRenderedText = liftIO . draw
, sizeOfRenderedText = tsz
}
newDefaultFontStore
:: ( MonadIO m
, MonadError TypograffitiError m
, Integral i
)
=> IO (V2 i)
-> m (FontStore [TextTransform])
newDefaultFontStore getDims = do
aw <- makeDefaultAllocateWord getDims
let dat = TextRenderingData
{ textRenderingDataAllocWord = aw
, textRenderingDataFontMap = mempty
, textRenderingDataCharSet = S.fromList asciiChars
}
FontStore
<$> liftIO (atomically $ newTMVar dat)
allocFont
:: ( MonadIO m
, MonadError TypograffitiError m
, Layout t
)
=> FontStore t
-> FilePath
-> GlyphSize
-> m (Font t)
allocFont store file sz = do
let mvar = unFontStore store
s <- liftIO $ atomically $ takeTMVar mvar
atlas <-
allocAtlas
file
sz
$ S.toList
$ textRenderingDataCharSet s
let fontmap = textRenderingDataFontMap s
font = Font
{ fontAtlas = atlas
, fontWordCache = mempty
}
liftIO
$ atomically
$ putTMVar mvar
$ s{ textRenderingDataFontMap = M.insert (file, sz) font fontmap }
return font