packages feed

animate-preview-0.1.2.3: library/Animate/Preview/Resource.hs

module Animate.Preview.Resource where

import qualified SDL
import qualified SDL.Font as Font
import qualified SDL.Image as Image
import qualified Animate
import qualified SDL.Raw.Video as Raw
import qualified SDL.Internal.Numbered as Numbered
import qualified Data.Map as Map
import Control.Monad (filterM)
import Data.Text (Text)
import Data.Maybe (catMaybes)
import Data.StateVar (($=))
import SDL.Vect
import System.IO.Error (catchIOError)
import Paths_animate_preview (getDataFileName)

import Animate.Preview.Chars

data Resources = Resources
  { rFont :: Font.Font
  , rGlyphMap :: Map.Map Char Glyph
  , rGlyphSize :: Int
  }

data Glyph = Glyph
  { gChar :: Char
  , gMetrics :: (Int, Int, Int, Int, Int)
  , gTexture :: SDL.Texture
  }

-- | Produce a new 'SDL.Surface' based on an existing one, but
-- optimized for blitting to the specified 'SDL.PixelFormat'.
convertSurface :: SDL.Surface -> SDL.PixelFormat -> IO SDL.Surface
convertSurface (SDL.Surface s _) pixFmt = do
  fmt <- Raw.allocFormat (Numbered.toNumber pixFmt)
  surface <- SDL.Surface <$> Raw.convertSurface s fmt 0 <*> pure Nothing
  surface <$ Raw.freeFormat fmt

loadSurface' :: FilePath -> Maybe Animate.Color -> IO (Maybe SDL.Surface)
loadSurface' path alpha = do
  surface0' <- catchIOError
    (Just <$> Image.load path)
    (const $ return Nothing)
  case surface0' of
    Nothing -> return Nothing
    Just surface0 -> do
      surface <- convertSurface surface0 SDL.RGBA8888
      SDL.freeSurface surface0
      case alpha of
        Just (r,g,b) -> SDL.surfaceColorKey surface $= (Just $ V4 r g b 0x00)
        Nothing -> return ()
      return (Just surface)

alphaColorDef :: Animate.Color
alphaColorDef = (0xff,0x00,0xff)

createText :: Bool -> SDL.Renderer -> Font.Font -> Text -> IO SDL.Texture
createText highDpi ren font text = do
  Font.setHinting font Font.None
  Font.setOutline font (if highDpi then 4 else 2)
  outline <- Font.solid font (V4 0 0 0 0) text
  Font.setOutline font 0
  inline <- Font.solid font (V4 255 255 255 255) text
  _ <- SDL.surfaceBlit inline Nothing outline (Just $ SDL.P (if highDpi then 4 else 2))
  tex <- SDL.createTextureFromSurface ren outline
  SDL.freeSurface inline
  SDL.freeSurface outline
  return tex

createGlyph :: Bool -> SDL.Renderer -> Font.Font -> Char -> IO (Maybe Glyph)
createGlyph highDpi ren font ch = do
  metrics' <- Font.glyphMetrics font ch
  --
  case metrics' of
    Nothing -> return Nothing
    Just metrics@(xmin, xmax, _ymin, _ymax, _adv) -> do
      if (xmax - xmin > 0) || ch == ' '
        then do
          Font.setHinting font Font.None
          Font.setOutline font (if highDpi then 4 else 2)
          outline <- Font.solidGlyph font (V4 0 0 0 0) ch
          Font.setOutline font 0
          inline <- Font.solidGlyph font (V4 255 255 255 255) ch
          _ <- SDL.surfaceBlit inline Nothing outline (Just $ SDL.P (if highDpi then 4 else 2))
          tex <- SDL.createTextureFromSurface ren outline
          SDL.freeSurface inline
          SDL.freeSurface outline
          --
          return . Just $ Glyph ch metrics tex
        else return Nothing

loadResources :: Bool -> SDL.Renderer -> IO Resources
loadResources highDpi ren = do
  fileName <- getDataFileName "resource/ProggyClean.ttf"
  let glyphSize = if highDpi then 32 else 16
  font <- Font.load fileName glyphSize
  {-
  putStrLn "Font cache: filtering glyphs"
  availableChars <- filterM (Font.glyphProvided font) [minBound..maxBound]
  putStrLn "Font cache: creating textures"
  glyphs <- mapM (createGlyph highDpi ren font) (' ':availableChars)
  -}
  putStrLn "Font caching..."
  glyphs <- mapM (createGlyph highDpi ren font) filteredChars
  let glyphs' = map (\g -> (gChar g, g)) (catMaybes glyphs)
  return Resources
    { rFont = font
    , rGlyphMap = Map.fromList glyphs'
    , rGlyphSize = glyphSize
    }

freeResources :: Resources -> IO ()
freeResources r = do
  Font.free (rFont r)