{-# OPTIONS -O #-} -- Example of an drawing graphics onto a canvas. import Graphics.UI.Gtk import Graphics.UI.Gtk.Gdk.GC import Data.Array.MArray import Data.Word import Data.IORef import Control.Monad ( when ) import Control.Monad.Trans ( liftIO ) import Data.Array.Base ( unsafeWrite ) main = do initGUI dia <- dialogNew dialogAddButton dia stockOk ResponseOk contain <- dialogGetUpper dia canvas <- drawingAreaNew canvas `on` sizeRequest $ return (Requisition 256 256) -- create the Pixbuf pb <- pixbufNew ColorspaceRgb False 8 256 256 pbData <- (pixbufGetPixels pb :: IO (PixbufData Int Word8)) row <- pixbufGetRowstride pb chan <- pixbufGetNChannels pb bits <- pixbufGetBitsPerSample pb putStrLn ("bytes per row: "++show row++", channels per pixel: "++show chan++ ", bits per sample: "++show bits) -- draw into the Pixbuf doFromTo 0 255 $ \y -> doFromTo 0 255 $ \x -> do writeArray pbData (x*chan+y*row) (fromIntegral x) writeArray pbData (1+x*chan+y*row) (fromIntegral y) writeArray pbData (2+x*chan+y*row) 0 -- a function to update the Pixbuf blueRef <- newIORef 0 dirRef <- newIORef True let updateBlue = do blue <- readIORef blueRef --print blue doFromTo 0 255 $ \y -> doFromTo 0 255 $ \x -> -- Here, writeArray was replaced with unsafeWrite. The latter does -- not check that the index is within bounds which has a tremendous -- effect on performance. -- writeArray pbData (2+x*chan+y*row) blue -- safe checked indexing unsafeWrite pbData (2+x*chan+y*row) blue -- unchecked indexing -- arrange for the canvas to be redrawn now that we've changed -- the Pixbuf widgetQueueDraw canvas -- update the blue state ready for next time dir <- readIORef dirRef let diff = 4 let blue' = if dir then blue+diff else blue-diff if dir then if blue<=maxBound-diff then writeIORef blueRef blue' else writeIORef blueRef maxBound >> modifyIORef dirRef not else if blue>=minBound+diff then writeIORef blueRef blue' else writeIORef blueRef minBound >> modifyIORef dirRef not return True idleAdd updateBlue priorityLow canvas `on` exposeEvent $ updateCanvas pb boxPackStartDefaults contain canvas widgetShow canvas dialogRun dia return () updateCanvas :: Pixbuf -> EventM EExpose Bool updateCanvas pb = do win <- eventWindow region <- eventRegion liftIO $ do gc <- gcNew win width <- pixbufGetWidth pb height <- pixbufGetHeight pb pbregion <- regionRectangle (Rectangle 0 0 width height) regionIntersect region pbregion rects <- regionGetRectangles region -- putStrLn ("redrawing: "++show rects) (flip mapM_) rects $ \(Rectangle x y w h) -> do drawPixbuf win gc pb x y x y w h RgbDitherNone 0 0 return True -- GHC is much better at opimising loops like this: -- -- > doFromTo 0 255 $ \y -> -- > doFromTo 0 255 $ \x -> do ... -- -- Than it is at optimising loops like this: -- -- > sequence_ [ do ... -- > | x <- [0..255] -- > , y <- [0..255] ] -- -- The first kind of loop runs significantly faster (with GHC 6.2 and 6.4) {-# INLINE doFromTo #-} -- do the action for [from..to], ie it's inclusive. doFromTo :: Int -> Int -> (Int -> IO ()) -> IO () doFromTo from to action = let loop n | n > to = return () | otherwise = do action n loop (n+1) in loop from