2011-03-13 79 views
1

有一个这样的例子。我怎么能用gtk和haskell做2d双缓冲。我想将原语渲染到屏幕外的缓冲区并翻转。这段代码只渲染一个像素/矩形。我想使用双缓冲方法添加移动。Haskell GTK,双缓冲与原语

import Graphics.UI.Gtk 
import Graphics.UI.Gtk.Gdk.GC 
import Graphics.UI.Gtk hiding (Color, Point, Object) 

defaultFgColor :: Color 
defaultFgColor = Color 65535 65535 65535 

defaultBgColor :: Color 
defaultBgColor = Color 0 0 0 

renderScene d ev = do 
    dw  <- widgetGetDrawWindow d 
    (w, h) <- widgetGetSize d 
    gc  <- gcNew dw 
    let fg = Color (round (65535 * 205)) 
        (round (65535 * 0)) 
        (round (65535 * 0)) 
    gcSetValues gc $ newGCValues { foreground = fg } 
    drawPoint dw gc (120, 120) 
    drawPoint dw gc (22, 22) 
    drawRectangle dw gc True 20 20 20 20 
    return True 

main :: IO() 
main = do 
    initGUI 
    window <- windowNew 
    drawing <- drawingAreaNew 
    windowSetTitle window "Cells" 
    containerAdd window drawing 
    let bg = Color (round (65535 * 205)) 
        (round (65535 * 205)) 
        (round (65535 * 255)) 
    widgetModifyBg drawing StateNormal bg 
    onExpose drawing (renderScene drawing) 

    onDestroy window mainQuit 
    windowSetDefaultSize window 800 600 
    windowSetPosition window WinPosCenter 
    widgetShowAll window 
    mainGUI 
+0

你能告诉我,什么不适合你吗? – fuz 2011-03-13 15:34:46

+0

该代码只渲染一个像素。我想使用双缓冲方法添加移动。 – 2011-03-14 17:31:27

回答

2

这是我使用的是开罗画绘图区域,避免 闪烁的东西。尝试将此代码添加到您的renderScene功能:

-- Get the draw window (dw) and its size (w,h) 
    -- ... 

    regio <- regionRectangle $ Rectangle 0 0 w h 
    drawWindowBeginPaintRegion dw regio 

    -- Put paiting code here 
    -- .. 

    drawWindowEndPaint dw 

你的最终代码看起来是这样的:

import Graphics.UI.Gtk 
import Graphics.UI.Gtk.Gdk.GC 
import Graphics.UI.Gtk hiding (Color, Point, Object) 
import Data.IORef 

defaultFgColor :: Color 
defaultFgColor = Color 65535 65535 65535 

defaultBgColor :: Color 
defaultBgColor = Color 0 0 0 

renderScene pref d _ev = renderScene' pref d 

renderScene' :: IORef Int -> DrawingArea -> IO Bool 
renderScene' pref d = do 
    dw  <- widgetGetDrawWindow d 
    (w, h) <- widgetGetSize d 
    regio <- regionRectangle $ Rectangle 0 0 w h 

    pos <- readIORef pref 
    -- Go around, CCW, in a circle of size 20, centered at (100,100) 
    let x = 100 + round (20 * sin (fromIntegral pos * pi * 2/360)) 
     y = 100 + round (20 * cos (fromIntegral pos * pi * 2/360)) 
     pos' = (pos + 1) `mod` 360 
    writeIORef pref pos' 

    drawWindowBeginPaintRegion dw regio 
    gc  <- gcNew dw 
    let fg = Color (round (65535 * 205)) 
        (round (65535 * 0)) 
        (round (65535 * 0)) 
    gcSetValues gc $ newGCValues { foreground = fg } 
    drawPoint dw gc (120, 120) 
    drawPoint dw gc (22, 22) 
    drawRectangle dw gc True x y 20 20 
    -- Paint an extra rectangle 
    drawRectangle dw gc True 200 200 200 200 
    drawWindowEndPaint dw 
    return True 

main :: IO() 
main = do 
    initGUI 
    window <- windowNew 
    drawing <- drawingAreaNew 
    windowSetTitle window "Cells" 
    containerAdd window drawing 
    let bg = Color (round (65535 * 205)) 
        (round (65535 * 205)) 
        (round (65535 * 255)) 
    widgetModifyBg drawing StateNormal bg 

    pref <- newIORef 0 

    onExpose drawing (renderScene pref drawing) 
    timeoutAdd (renderScene' pref drawing) 10 

    onDestroy window mainQuit 
    windowSetDefaultSize window 800 600 
    windowSetPosition window WinPosCenter 
    widgetShowAll window 
    mainGUI 
+0

看起来很有趣。你有没有更多的代码。我看不到交换。 – 2011-03-15 14:03:24

+1

交换发生在drawWindowEndPaint。根据文档,它“表示最近调用drawWindowBeginPaintRegion创建的后备存储应该被复制到屏幕上并被删除”。 – 2011-03-15 15:11:40

+0

我用你可以使用的代码修改了我原来的评论。它画一个方形的圆圈,另一个只是躺在那里。如果您不使用paintRegions并清除背景(例如绘制白色矩形),则会看到闪烁。这样,当你调用drawWindowEndPaint时,所有东西都被绘制了。 – 2011-03-15 15:54:42

0

这可能是一个想法,看看ThreadScope。滚动在那里实现,非常接近双缓冲。这里是什么,我认为他们做一个简化版本:

prev_surface <- readIORef prevView 
win <- widgetGetDrawWindow timelineDrawingArea 
renderWithDrawable win $ do 

    -- Create new surface based on the old one 
    new_surface <- liftIO $ createSimilarSurface [...] 
    renderWith new_surface $ do 
    setSourceSurface prev_surface off 0 
    Cairo.rectangle [...] 
    Cairo.fill 
    [... render newly exposed stuff ...] 
    surfaceFinish new_surface 

    -- Save back new view 
    liftIO $ writeIORef prevView new_surface 

    -- Paint new view 
    setSourceSurface new_surface 0 0 
    setOperator OperatorSource 
    paint 

实际的代码可以在Timeline/Render.hs找到。不知道这是否是最好的方法,但它在实践中似乎运作良好。我希望这有帮助。