gtk-0.11.1: demo/fastdraw/FastDraw.hs
{-# 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