carettah-0.0.6: Render.hs
module Render (clearCanvas, CairoPosition(..), CairoSize(..), toDouble,
renderWave, renderTurtle, renderPngFit, renderPngInline,
renderTextM, renderTextG, renderLayoutG, yposSequence,
renderSlide) where
import Data.Char
import Data.Bits
import System.FilePath ((</>),(<.>))
import Control.Monad.Reader
import qualified Graphics.UI.Gtk as G
import qualified Graphics.Rendering.Cairo as C
--
import Config
import WrapPaths (wrapGetDataFileName)
data CairoPosition = CairoPosition Double | CairoCenter
deriving (Show, Eq, Ord)
data CairoSize = CairoSize Double | CairoFit
deriving (Show, Eq, Ord)
-- copy from System.Glib.UTFString (gtk2hs/glib/System/Glib/UTFString.hs)
-- 本来はCStringを使うとこに埋め込んどくべき。gtk2hsを参考に
toUTF :: String -> String
toUTF [] = []
toUTF (x:xs) | ord x<=0x007F = x:toUTF xs
| ord x<=0x07FF = chr (0xC0 .|. ((ord x `shift` (-6)) .&. 0x1F)):
chr (0x80 .|. (ord x .&. 0x3F)):
toUTF xs
| otherwise = chr (0xE0 .|. ((ord x `shift` (-12)) .&. 0x0F)):
chr (0x80 .|. ((ord x `shift` (-6)) .&. 0x3F)):
chr (0x80 .|. (ord x .&. 0x3F)):
toUTF xs
toDouble :: Integral a => a -> Double
toDouble = fromIntegral
mySetFontSize :: String -> Double -> C.Render ()
mySetFontSize fname fsize = do
C.selectFontFace (toUTF fname) C.FontSlantNormal C.FontWeightNormal
C.setFontSize fsize
renderText' :: String -> CairoPosition -> CairoPosition -> Double -> String -> C.Render Double
renderText' fname x y fsize text = do
C.save
mySetFontSize fname fsize
(C.TextExtents _ yb w h _ _) <- C.textExtents (toUTF text)
C.restore
let truePosition (CairoPosition x') (CairoPosition y') = return (x', y')
truePosition CairoCenter (CairoPosition y') =
return (toDouble (canvasW gCfg) / 2 - w / 2, y')
truePosition x' y' =
error $ "called with x=" ++ show x' ++ " y=" ++ show y'
(xt, yt) <- truePosition x y
let nypos = yt + h - yb
C.save
mySetFontSize fname fsize
C.moveTo xt nypos
C.textPath $ toUTF text
C.fill >> C.stroke >> C.restore
return nypos
renderTextG :: CairoPosition -> CairoPosition -> Double -> String -> C.Render Double
renderTextG = renderText' "TakaoExゴシック"
renderTextM :: CairoPosition -> CairoPosition -> Double -> String -> C.Render Double
renderTextM = renderText' "Takao P明朝"
renderLayoutG :: CairoPosition -> CairoPosition -> Double -> String -> C.Render Double
renderLayoutG x y fsize text = do
C.save
ctxt <- liftIO $ G.cairoCreateContext Nothing
txt <- liftIO $ G.layoutEmpty ctxt
liftIO $ G.layoutSetText txt text
let truePosition (CairoPosition x') (CairoPosition y') = return (x', y' + 20)
truePosition x' y' =
error $ "called with x=" ++ show x' ++ " y=" ++ show y'
(xt, yt) <- truePosition x y
C.moveTo xt yt
fd <- liftIO G.fontDescriptionNew
liftIO $ G.fontDescriptionSetSize fd fsize
liftIO $ G.layoutSetFontDescription txt (Just fd)
(_, G.PangoRectangle _ _ _ lh) <-
liftIO $ G.layoutGetExtents txt -- xxx inkとlogicalの違いは?
G.showLayout txt
C.restore
return $ yt + lh
renderSurface :: Double -> Double -> Double -> C.Surface -> C.Render ()
renderSurface x y alpha surface = do
C.save
C.setSourceSurface surface x y
C.paintWithAlpha alpha
C.restore
pngSurfaceSize :: FilePath -> C.Render (C.Surface, Int, Int)
pngSurfaceSize file = do
surface <- liftIO $ C.imageSurfaceCreateFromPNG file
w <- C.imageSurfaceGetWidth surface
h <- C.imageSurfaceGetHeight surface
return (surface, w, h)
renderPngSize :: Double -> Double -> Double -> Double -> Double -> FilePath -> C.Render Double
renderPngSize = f
where f x y w h alpha file = do
C.save
(surface, iw, ih) <- pngSurfaceSize file
let xscale = w / toDouble iw
let yscale = h / toDouble ih
C.scale xscale yscale
renderSurface (x / xscale) (y / yscale) alpha surface
C.surfaceFinish surface
C.restore
return $ y + h
renderPngInline :: CairoPosition -> CairoPosition -> CairoSize -> CairoSize -> Double -> FilePath -> C.Render Double
renderPngInline = f
where f CairoCenter (CairoPosition y) CairoFit CairoFit alpha file = do
C.save
(surface, iw, ih) <- pngSurfaceSize file
let diw = toDouble iw
dih = toDouble ih
cw = toDouble (canvasW gCfg)
ch = toDouble (canvasH gCfg)
wratio = cw / diw
hratio = (ch - y) / dih
scale = if wratio > hratio then hratio * 0.95 else wratio * 0.95
tiw = diw * scale
tih = dih * scale
y' = y + 10
C.scale scale scale
renderSurface ((cw / 2 - tiw / 2) / scale) (y' / scale) alpha surface
C.surfaceFinish surface
C.restore
return $ y' + tih
f _ _ _ _ _ _ = return 0 -- xxx renerPngFit統合して一関数にすべき
renderPngFit :: Double -> FilePath -> C.Render ()
renderPngFit = f
where f alpha file = do
C.save
(surface, iw, ih) <- pngSurfaceSize file
let cw = toDouble $ canvasW gCfg
ch = toDouble $ canvasH gCfg
C.scale (cw / toDouble iw) (ch / toDouble ih)
renderSurface 0 0 alpha surface
C.surfaceFinish surface
C.restore
clearCanvas :: Int -> Int -> C.Render ()
clearCanvas w h = do
C.save
C.setSourceRGB 1 1 1
C.rectangle 0 0 (toDouble w) (toDouble h)
C.fill >> C.stroke >> C.restore
-- xxx プレゼン時間に応じて波表示
renderWave :: C.Render ()
renderWave = do
sec <- liftIO elapsedSecFromStart
smin <- queryCarettahState speechMinutes
let ws = waveSize gCfg
ch = toDouble $ canvasH gCfg
speechSec = 60 * smin
charMax = waveCharMax gCfg
numChar = round $ charMax * sec / speechSec
_ <- renderTextM (CairoPosition 0) (CairoPosition $ ch - ws) ws $ replicate numChar '>'
return ()
renderTurtle :: Double -> C.Render ()
renderTurtle progress = do
fn <- liftIO . wrapGetDataFileName $ "data" </> "turtle" <.> "png"
renderPngSize (ts / 2 + (cw - ts * 2) * progress) (ch - ts) ts ts 1 fn >> return ()
where ts = turtleSize gCfg
cw = toDouble $ canvasW gCfg
ch = toDouble $ canvasH gCfg
yposSequence :: Double -> [Double -> C.Render Double] -> C.Render Double
yposSequence ypos (x:xs) = x ypos >>= (`yposSequence` xs)
yposSequence ypos [] = return ypos
renderSlideFilter :: Int -> Int -> [Double -> C.Render Double] -> C.Render ()
renderSlideFilter w h s = do
clearCanvas w h
let cw = toDouble $ canvasW gCfg
ch = toDouble $ canvasH gCfg
tcy = textContextY gCfg
C.scale (toDouble w / cw) (toDouble h / ch)
_ <- yposSequence tcy s
renderWave
renderSlide :: [[Double -> C.Render Double]] -> Int -> Int -> Int -> C.Render ()
renderSlide s p w h = do
renderSlideFilter w h (s !! p)
renderTurtle $ toDouble p / toDouble (length s - 1)