{-# LANGUAGE RecordWildCards #-} import GEGL import qualified BABL as B import qualified SDL import qualified SDL.Raw as Raw import Foreign.C.Types import Foreign.Ptr import Foreign.Storable import Control.Concurrent import Control.Monad import Control.Monad.Loops import Data.Maybe (fromMaybe) import Debug.Trace main :: IO () main = do SDL.initialize [SDL.InitVideo, SDL.InitTimer] traceIO "SDL init" window <- SDL.createWindow "GEGL example02: SDL2" SDL.defaultWindow surface <- SDL.getWindowSurface window gegl_init traceIO "GEGL init" let sdl_raw_format = B.PixelFormat B.RGBA B.CFu8 sdl_format <- B.babl_format sdl_raw_format 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" SDL.V2 (CInt rw) (CInt rh) <- SDL.surfaceDimensions surface let (w, h) = (fromIntegral rw, fromIntegral rh) _ <- gegl_buffer_set_extent buffer $ GeglRectangle 0 0 w h pixels <- SDL.surfacePixels surface let SDL.Surface rawSurfacePtr _ = surface rawSurface <- peek rawSurfacePtr pixelFormat <- peek $ Raw.surfaceFormat rawSurface let pitch = fromIntegral (Raw.pixelFormatBytesPerPixel pixelFormat) * w :: Int traceIO $ "pitch is: " ++ show pitch ld <- return $ LoopData surface window (w, h) pitch pixels buffer over root sdl_format sdl_raw_format traceIO "first update" 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) mapM_ gegl_node_drop [root, background, over] gegl_exit SDL.quit traceIO "bye bye" data LoopData = LoopData { ldSurface :: SDL.Surface , ldWindow :: SDL.Window , ldSurfDim :: (Int, Int) , ldPitch :: Int , ldPixels :: Ptr () , ldBuffer :: GeglBuffer , ldNode :: GeglNode , ldGraph :: GeglNode , ldFormat :: B.BablFormatPtr , ldBablFormat :: B.PixelFormat } type UpdateData = (GeglRectangle, LoopData) updateSurface :: UpdateData -> IO () updateSurface (roi, LoopData{..}) = do -- traceIO "locking surface" SDL.lockSurface ldSurface let surfaceRect = uncurry (GeglRectangle 0 0) ldSurfDim sroi@(GeglRectangle ix iy iw ih) <- fromMaybe surfaceRect <$> gegl_rectangle_intersect surfaceRect roi let origin = ldPixels `plusPtr` (ix * B.babl_components_per_pixel ldBablFormat + iy * ldPitch) -- traceIO "blit" gegl_node_blit ldNode 1 sroi ldFormat origin ldPitch [GeglBlitDefault] -- traceIO "unlocking surface" SDL.unlockSurface ldSurface -- traceIO "update window" SDL.updateWindowSurface ldWindow -- | Window update loop loop :: LoopData -> MVar Bool -> MVar Bool -> IO () loop ld down go = do e <- SDL.waitEvent case SDL.eventPayload e of SDL.MouseButtonEvent dat -> when (SDL.mouseButtonEventButton dat == SDL.ButtonLeft) $ if (SDL.mouseButtonEventMotion dat == SDL.Pressed) then do _ <- swapMVar down True let (SDL.P (SDL.V2 x y)) = SDL.mouseButtonEventPos dat drawCircle ld (fromIntegral x) (fromIntegral y) else do _ <- swapMVar down False return () SDL.MouseMotionEvent dat -> do isDown <- readMVar down when isDown $ do let (SDL.P (SDL.V2 x y)) = SDL.mouseMotionEventPos dat drawCircle ld (fromIntegral x) (fromIntegral y) SDL.WindowClosedEvent _ -> do traceIO "seeya!" _ <- swapMVar go False return () _ -> return () 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)