hoodle-render-0.3.2: src/Graphics/Hoodle/Render.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
-----------------------------------------------------------------------------
-- |
-- Module : Graphics.Hoodle.Render
-- Copyright : (c) 2011-2013 Ian-Woo Kim
--
-- License : BSD3
-- Maintainer : Ian-Woo Kim <ianwookim@gmail.com>
-- Stability : experimental
-- Portability : GHC
--
-- collection of rendering routine
--
-----------------------------------------------------------------------------
module Graphics.Hoodle.Render
(
-- * simple rendering using non-R-structure
renderStrk
, renderImg
, renderBkg
, renderItem
, renderPage
-- * render in bbox using non R-structure
-- , renderBkg_InBBox
-- * simple rendering using R-structure
, renderRBkg
, renderRItem
-- * render in bbox
, renderRLayer_InBBox
, renderRBkg_InBBox
-- * render using buf
, renderRBkg_Buf
, renderRLayer_InBBoxBuf
-- * buffer update
, updateLayerBuf
, updatePageBuf
, updateHoodleBuf
-- * construct R-structure from non-R-structure
, cnstrctRLayer
, cnstrctRBkg_StateT
, cnstrctRPage_StateT
, cnstrctRHoodle
) where
import Control.Lens (view,set)
import Control.Monad.Identity (runIdentity)
import Control.Monad.State hiding (mapM,mapM_)
import qualified Data.ByteString.Char8 as C
import Data.Foldable
import Data.Traversable (mapM)
-- import qualified Data.Map as M
-- import Data.Monoid
import Graphics.Rendering.Cairo
-- from hoodle-platform
import Data.Hoodle.Generic
import Data.Hoodle.Simple
import Data.Hoodle.BBox
import Data.Hoodle.Predefined
import Data.Hoodle.Zipper hiding (moveTo)
import qualified Graphics.UI.Gtk.Poppler.Page as PopplerPage
import qualified Graphics.Rendering.Cairo.SVG as RSVG
-- from this package
-- import Graphics.Hoodle.Render.Simple
import Graphics.Hoodle.Render.Background
import Graphics.Hoodle.Render.Item
import Graphics.Hoodle.Render.Primitive
import Graphics.Hoodle.Render.Type
import Graphics.Hoodle.Render.Type.HitTest
import Graphics.Hoodle.Render.Util
import Graphics.Hoodle.Render.Util.HitTest
--
import Prelude hiding (curry,uncurry,mapM,mapM_,concatMap)
-----
-- simple
---
-- | render stroke
renderStrk :: Stroke -> Render ()
renderStrk s@(Stroke _ _ w d) = do
let opacity = if stroke_tool s == "highlighter"
then predefined_highlighter_opacity
else 1.0
case getPenColor (stroke_color s) of
Just (r,g,b,a) -> setSourceRGBA r g b (a*opacity)
Nothing -> setSourceRGBA 0.5 0.5 0.5 1
setLineWidth w
setLineCap LineCapRound
setLineJoin LineJoinRound
drawStrokeCurve d
stroke
renderStrk s@(VWStroke _ _ d) = do
let opacity = if stroke_tool s == "highlighter"
then predefined_highlighter_opacity
else 1.0
case getPenColor (stroke_color s) of
Just (r,g,b,a) -> setSourceRGBA r g b (a*opacity)
Nothing -> setSourceRGBA 0.5 0.5 0.5 1
setFillRule FillRuleWinding
drawVWStrokeCurve d
fill
-- | render image : not fully implemented
renderImg :: Image -> Render ()
renderImg (Image _ (x,y) (Dim w h)) = do
setSourceRGBA 0 0 0 1
setLineWidth 10
rectangle x y w h
stroke
-- | render svg
renderSVG :: SVG -> Render ()
renderSVG svg@(SVG _ _ bstr _ _) = do
let str = C.unpack bstr
RSVG.withSvgFromString str $ \rsvg -> do
let svgbbx = runIdentity (makeBBoxed svg)
let (x,y) = (svg_pos . bbxed_content) svgbbx
BBox (x1,y1) (x2,y2) = getBBox svgbbx
(ix',iy') = RSVG.svgGetSize rsvg
ix = fromIntegral ix'
iy = fromIntegral iy'
-- clipBBox (Just (getBBox svgbbx))
save
translate x y
scale ((x2-x1)/ix) ((y2-y1)/iy)
RSVG.svgRender rsvg
restore
-- resetClip
return ()
-- | render svg
renderLink :: Link -> Render ()
renderLink lnk = do
let bstr = link_render lnk
let str = C.unpack bstr
RSVG.withSvgFromString str $ \rsvg -> do
let lnkbbx = runIdentity (makeBBoxed lnk)
let (x,y) = (link_pos . bbxed_content) lnkbbx
BBox (x1,y1) (x2,y2) = getBBox lnkbbx
(ix',iy') = RSVG.svgGetSize rsvg
ix = fromIntegral ix'
iy = fromIntegral iy'
clipBBox (Just (getBBox lnkbbx))
save
translate x y
scale ((x2-x1)/ix) ((y2-y1)/iy)
RSVG.svgRender rsvg
restore
resetClip
return ()
-- | render item
renderItem :: Item -> Render ()
renderItem (ItemStroke strk) = renderStrk strk
renderItem (ItemImage img) = renderImg img
renderItem (ItemSVG svg) = renderSVG svg
renderItem (ItemLink lnk) = renderLink lnk
-- |
renderPage :: Page -> Render ()
renderPage page = do
renderBkg (view background page,view dimension page)
setLineCap LineCapRound
setLineJoin LineJoinRound
mapM_ (mapM renderItem . view items) . view layers $ page
stroke
-----
-- R-structure
----
drawFallBackBkg :: Dimension -> Render ()
drawFallBackBkg (Dim w h) = do
setSourceRGBA 1 1 1 1
rectangle 0 0 w h
fill
setSourceRGBA 0 0 0 1
setLineWidth 5
moveTo 0 0
lineTo w h
stroke
moveTo w 0
lineTo 0 h
stroke
-- |
renderRBkg :: (RBackground,Dimension) -> Render (RBackground,Dimension)
renderRBkg (r,dim) =
case r of
(RBkgSmpl _ _ _) ->
drawBkgAndRecord (renderBkg (rbkg2Bkg r,dim))
-- renderBkg (rbkg2Bkg r,dim) >> return (r,dim)
(RBkgPDF _ _ _ p _) ->
maybe (drawFallBackBkg dim >> return (r,dim))
(\pg -> drawBkgAndRecord (bkgPdfRender pg)) p
(RBkgEmbedPDF _ p _) ->
maybe (drawFallBackBkg dim >> return (r,dim))
(\pg -> drawBkgAndRecord (bkgPdfRender pg)) p
where
drawBkgAndRecord rdr = do
rdr
case rbkg_cairosurface r of
Nothing -> return ()
Just sfc -> liftIO $ renderWith sfc rdr
return (r,dim)
bkgPdfRender pg = do
let Dim w h = dim
setSourceRGBA 1 1 1 1
rectangle 0 0 w h
fill
PopplerPage.pageRender pg
-- |
renderRItem :: RItem -> Render RItem
renderRItem itm@(RItemStroke strk) = renderStrk (bbxed_content strk) >> return itm
renderRItem itm@(RItemImage img msfc) = do
case msfc of
Nothing -> renderImg (bbxed_content img)
Just sfc -> do
let (x,y) = (img_pos . bbxed_content) img
BBox (x1,y1) (x2,y2) = getBBox img
ix <- liftM fromIntegral (imageSurfaceGetWidth sfc)
iy <- liftM fromIntegral (imageSurfaceGetHeight sfc)
save
-- setAntialias AntialiasNone
translate x y
scale ((x2-x1)/ix) ((y2-y1)/iy)
setSourceSurface sfc 0 0
paint
restore
return itm
renderRItem itm@(RItemSVG svgbbx mrsvg) = do
case mrsvg of
Nothing -> renderSVG (bbxed_content svgbbx)
Just rsvg -> do
let (x,y) = (svg_pos . bbxed_content) svgbbx
BBox (x1,y1) (x2,y2) = getBBox svgbbx
(ix',iy') = RSVG.svgGetSize rsvg
ix = fromIntegral ix'
iy = fromIntegral iy'
-- clipBBox (Just (getBBox svgbbx))
save
-- setAntialias AntialiasNone
translate x y
scale ((x2-x1)/ix) ((y2-y1)/iy)
RSVG.svgRender rsvg
restore
-- resetClip
return ()
return itm
renderRItem itm@(RItemLink lnkbbx mrsvg) = do
case mrsvg of
Nothing -> renderLink (bbxed_content lnkbbx)
Just rsvg -> do
let (x,y) = (link_pos . bbxed_content) lnkbbx
BBox (x1,y1) (x2,y2) = getBBox lnkbbx
(ix',iy') = RSVG.svgGetSize rsvg
ix = fromIntegral ix'
iy = fromIntegral iy'
-- clipBBox (Just (getBBox lnkbbx))
save
-- setAntialias AntialiasNone
translate x y
scale ((x2-x1)/ix) ((y2-y1)/iy)
RSVG.svgRender rsvg
restore
-- resetClip
return ()
return itm
------------
-- InBBox --
------------
{-
renderRItem_InBBox :: Maybe BBox -> RItem -> Render RItem
renderRItem_InBBox _ itm@(RItemStroke strk) = renderStrk (bbxed_content strk) >> return itm
renderRItem_InBBox mbbox itm@(RItemImage img msfc) = do
case msfc of
Nothing -> renderImg (bbxed_content img)
Just sfc -> do
let (x,y) = (img_pos . bbxed_content) img
BBox (x1,y1) (x2,y2) = getBBox img
ix <- liftM fromIntegral (imageSurfaceGetWidth sfc)
iy <- liftM fromIntegral (imageSurfaceGetHeight sfc)
save
clipBBox (fmap (flip inflate 1) mbbox)
translate x y
scale ((x2-x1)/ix) ((y2-y1)/iy)
setSourceSurface sfc 0 0
paint
resetClip
restore
return itm
renderRItem_InBBox mbbox itm@(RItemSVG svgbbx mrsvg) = do
case mrsvg of
Nothing -> renderSVG (bbxed_content svgbbx)
Just rsvg -> do
let (x,y) = (svg_pos . bbxed_content) svgbbx
BBox (x1,y1) (x2,y2) = getBBox svgbbx
(ix',iy') = RSVG.svgGetSize rsvg
ix = fromIntegral ix'
iy = fromIntegral iy'
clipBBox (fmap (flip inflate 0.5) mbbox)
save
translate x y
scale ((x2-x1)/ix) ((y2-y1)/iy)
RSVG.svgRender rsvg
resetClip
restore
return ()
return itm
renderRItem_InBBox mbbox itm@(RItemLink lnkbbx mrsvg) = do
case mrsvg of
Nothing -> renderLink (bbxed_content lnkbbx)
Just rsvg -> do
let (x,y) = (link_pos . bbxed_content) lnkbbx
BBox (x1,y1) (x2,y2) = getBBox lnkbbx
(ix',iy') = RSVG.svgGetSize rsvg
ix = fromIntegral ix'
iy = fromIntegral iy'
save
clipBBox (fmap (flip inflate 0.5) mbbox)
translate x y
scale ((x2-x1)/ix) ((y2-y1)/iy)
RSVG.svgRender rsvg
resetClip
restore
return ()
return itm
-}
-- | background drawing in bbox
renderRBkg_InBBox :: Maybe BBox
-> (RBackground,Dimension)
-> Render (RBackground,Dimension)
renderRBkg_InBBox mbbox (b,dim) = do
clipBBox (fmap (flip inflate 1) mbbox)
-- clipBBox mbbox
renderRBkg_Buf (b,dim)
resetClip
return (b,dim)
-- | render RLayer within BBox after hittest items
renderRLayer_InBBox :: Maybe BBox -> RLayer -> Render RLayer
renderRLayer_InBBox mbbox layer = do
clipBBox (fmap (flip inflate 2) mbbox) -- temporary
-- clipBBox mbbox
let hittestbbox = case mbbox of
Nothing -> NotHitted []
:- Hitted (view gitems layer)
:- Empty
Just bbox -> (hltHittedByBBox bbox . view gitems) layer
(mapM_ renderRItem . concatMap unHitted . getB) hittestbbox
resetClip
-- simply twice rendering if whole redraw happening
case view gbuffer layer of
LyBuf (Just sfc) -> do
liftIO $ renderWith sfc $ do
clipBBox (fmap (flip inflate 2) mbbox ) -- temporary
-- clipBBox mbbox
-- setAntialias AntialiasNone
setSourceRGBA 0 0 0 0
setOperator OperatorSource
paint
setOperator OperatorOver
(mapM_ renderRItem . concatMap unHitted . getB) hittestbbox
resetClip
return layer
_ -> return layer
-----------------------
-- draw using buffer --
-----------------------
-- | Background rendering using buffer
renderRBkg_Buf :: (RBackground,Dimension)
-> Render (RBackground,Dimension)
renderRBkg_Buf (b,dim) = do
case b of
RBkgSmpl _ _ msfc -> do
case msfc of
Nothing -> renderRBkg (b,dim) >> return ()
Just sfc -> do
save
-- setAntialias AntialiasNone
setSourceSurface sfc 0 0
paint
restore
RBkgPDF _ _ _n _ msfc -> do
case msfc of
Nothing -> renderRBkg (b,dim) >> return ()
Just sfc -> do
save
-- setAntialias AntialiasNone
setSourceSurface sfc 0 0
paint
restore
RBkgEmbedPDF _ _ msfc -> do
case msfc of
Nothing -> renderRBkg (b,dim) >> return ()
Just sfc -> do
save
-- setAntialias AntialiasNone
setSourceSurface sfc 0 0
paint
restore
return (b,dim)
-- |
renderRLayer_InBBoxBuf :: Maybe BBox -> RLayer -> Render RLayer
renderRLayer_InBBoxBuf mbbox lyr = do
case view gbuffer lyr of
LyBuf (Just sfc) -> do
clipBBox mbbox
setSourceSurface sfc 0 0
paint
resetClip
return lyr
_ -> do
renderRLayer_InBBox mbbox lyr
-- return lyr
-------------------
-- update buffer
-------------------
-- |
updateLayerBuf :: Dimension -> Maybe BBox -> RLayer -> IO RLayer
updateLayerBuf (Dim w h) mbbox lyr = do
case view gbuffer lyr of
LyBuf (Just sfc) -> do
renderWith sfc $ do
renderRLayer_InBBox mbbox lyr
return lyr
LyBuf Nothing -> do
-- return lyr
sfc <- createImageSurface FormatARGB32 (floor w) (floor h)
renderWith sfc $ do
renderRLayer_InBBox Nothing lyr
return (set gbuffer (LyBuf (Just sfc)) lyr)
-- |
updatePageBuf :: RPage -> IO RPage
updatePageBuf pg = do
let dim = view gdimension pg
mbbox = Just . dimToBBox $ dim
nlyrs <- mapM (updateLayerBuf dim mbbox) . view glayers $ pg
return (set glayers nlyrs pg)
-- |
updateHoodleBuf :: RHoodle -> IO RHoodle
updateHoodleBuf hdl = do
let pgs = view gpages hdl
npgs <- mapM updatePageBuf pgs
return . set gpages npgs $ hdl
-------
-- smart constructor for R hoodle structures
-------
-- |
cnstrctRHoodle :: Hoodle -> IO RHoodle
cnstrctRHoodle hdl = do
let hid = view hoodleID hdl
ttl = view title hdl
revs = view revisions hdl
pgs = view pages hdl
embeddedsrc = view embeddedPdf hdl
mdoc <- maybe (return Nothing) (\src -> liftIO $ popplerGetDocFromDataURI src)
embeddedsrc
npgs <- evalStateT (mapM cnstrctRPage_StateT pgs) (Just (Context "" "" Nothing mdoc))
return $ GHoodle hid ttl revs embeddedsrc (fromList npgs)
-- |
cnstrctRPage_StateT :: Page -> StateT (Maybe Context) IO RPage
cnstrctRPage_StateT pg = do
let bkg = view background pg
dim = view dimension pg
lyrs = view layers pg
nlyrs_lst <- liftIO $ mapM cnstrctRLayer lyrs
let nlyrs_nonemptylst = if null nlyrs_lst then (emptyRLayer,[]) else (head nlyrs_lst,tail nlyrs_lst)
nlyrs = fromNonEmptyList nlyrs_nonemptylst
nbkg <- cnstrctRBkg_StateT dim bkg
return $ GPage dim nbkg nlyrs
-- |
cnstrctRLayer :: Layer -> IO RLayer
cnstrctRLayer lyr = do
nitms <- (mapM cnstrctRItem . view items) lyr
return (set gitems nitms emptyRLayer)