packages feed

gegl-0.0.0.1: examples/example01.hs

{-# LANGUAGE RecordWildCards #-}

import qualified Graphics.UI.SDL as SDL
import qualified Graphics.UI.SDL.Types as SDL
import qualified BABL as B
import GEGL

import Control.Monad
import Control.Monad.Loops
import Control.Concurrent.MVar

import Foreign.C.Types
import Foreign.Ptr

import Data.Maybe (fromMaybe)

import Debug.Trace

main :: IO ()
main = do
  SDL.init [SDL.InitVideo, SDL.InitTimer]
  traceIO "SDL init"
  surface <- SDL.setVideoMode 800 600 24 [SDL.SWSurface]
  SDL.setCaption "GEGL example01: SDL" "example01"
  gegl_init
  traceIO "GEGL init"
  sdl_format <- B.babl_format $ B.PixelFormat B.R'G'B' B.CFu8
  root <- gegl_node_new
  buffer <- gegl_buffer_new (Just $ GeglRectangle 0 0 0 0) =<<
    B.babl_format (B.PixelFormat B.RGBA B.CFfloat)
  background <- gegl_node_new_child root $ checkerboardOperation
    [ Property "color1" $ PropertyColor $ RGBA 0.4 0.4 0.4 1
    , Property "color2" $ PropertyColor $ RGBA 0.6 0.6 0.6 1
    ]
  over <- gegl_node_new_child root defaultOverOperation
  buffer_src <- gegl_node_new_child root $ bufferSourceOperation
    [ Property "buffer" $ PropertyBuffer buffer
    ]
  gegl_node_link background over
  gegl_node_connect_to buffer_src "output" over "aux"
  traceIO "Nodes constructed"
  let w = SDL.surfaceGetWidth surface
      h = SDL.surfaceGetHeight surface
  _ <- gegl_buffer_set_extent buffer $ GeglRectangle 0 0 w h
  pixels <- SDL.surfaceGetPixels surface
  ld <- return $ LoopData
    surface
    (w, h)
    (fromIntegral $ SDL.surfaceGetPitch surface)
    buffer
    over
    sdl_format
  traceIO "initial blit"
  updateSurface (GeglRectangle 0 0 w h, ld)
  go <- newMVar True
  butDown <- newMVar False
  traceIO "begin iteration"
  whileM_ (fromMaybe False <$> tryReadMVar go) (loop ld butDown go)
  gegl_exit
  SDL.quit
  traceIO "bye bye"

data LoopData = LoopData
  { ldSurface :: SDL.Surface
  , ldSurfDim :: (Int, Int)
  , ldPitch   :: Int
  -- , ldPixels  :: SDL.Pixels
  , ldBuffer  :: GeglBuffer
  , ldNode    :: GeglNode
  , ldFormat  :: B.BablFormatPtr
  }

type UpdateData = (GeglRectangle, LoopData)

-- | Window update loop
loop :: LoopData -> MVar Bool -> MVar Bool -> IO ()
loop ld down g = do
  e <- SDL.waitEvent
  case e of
    SDL.MouseButtonDown x y SDL.ButtonLeft -> do
      _ <- swapMVar down True
      drawCircle ld (fromIntegral x) (fromIntegral y)
    SDL.MouseMotion x y _ _ -> do
      isDown <- readMVar down
      when isDown $
        drawCircle ld (fromIntegral x) (fromIntegral y)
    SDL.MouseButtonUp _ _ SDL.ButtonLeft -> do
      _ <- swapMVar down False
      return ()
    SDL.Quit -> do
      putStrLn "seeya!"
      _ <- swapMVar g False
      return ()
    _                        -> return ()

updateSurface :: UpdateData -> IO ()
updateSurface (roi, LoopData{..}) = do
  SDL.lockSurface ldSurface
  pixels <- SDL.surfaceGetPixels ldSurface
  let output = uncurry (GeglRectangle 0 0) ldSurfDim
  sroi@(GeglRectangle ix iy iw ih) <- fromMaybe output <$>
    gegl_rectangle_intersect output roi
  let origin = pixels `plusPtr` (ix * 3 + iy * ldPitch)
  gegl_node_blit
    ldNode
    1
    sroi
    ldFormat
    origin
    ldPitch
    [GeglBlitDefault]
  SDL.unlockSurface ldSurface
  SDL.updateRect ldSurface $ SDL.Rect ix iy iw ih

drawCircle :: LoopData -> Int -> Int -> IO ()
drawCircle ld@LoopData{..} x y = do
  let r = 20
      roi = GeglRectangle (x - r) (y - r) (2 * r) (2 * r)
  pixelMap ldBuffer roi (B.PixelFormat B.RGBA B.CFfloat) GeglAccessReadWrite GeglAbyssNone
      (\(Pixel px py pc) ->
        let dsqr = ((x - px) ^ 2) + ((y - py) ^ 2)
            (CVfloat (CFloat pr), CVfloat (CFloat pg), CVfloat (CFloat pb), CVfloat (CFloat pa)) = pc
            dist = (sqrt (fromIntegral dsqr :: Float))
        in if dsqr < (r ^ 2)
          then
            if dist < fromIntegral (r - 1)
              then
                Pixel px py
                  ( CVfloat $ CFloat 0
                  , CVfloat $ CFloat 0
                  , CVfloat $ CFloat 0
                  , CVfloat $ CFloat $ if pa < 1 then 1 else pa
                  )
              else
                let alpha = (fromIntegral r - dist)
                    dst_a = pa
                    a = alpha + dst_a * (1 - alpha)
                    a_term = dst_a * (1 - alpha)
                    red = 0 * alpha + pr * a_term
                    gre = 0 * alpha + pg * a_term
                    blu = 0 * alpha + pb * a_term
                in Pixel px py
                  ( CVfloat $ CFloat $ red/a
                  , CVfloat $ CFloat $ gre/a
                  , CVfloat $ CFloat $ blu/a
                  , CVfloat $ CFloat $ if pa < alpha then alpha else pa
                  )
          else
            Pixel px py pc
      )
  updateSurface (roi, ld)