2013-01-09 44 views
8

我有一個應用程序使用OpenGL和GLUT在屏幕上顯示一些東西。我想訪問「逐個像素」顯示的顏色,但我無法理解所提供的方法。從OpenGL/GLUT讀取像素應用

好像訪問這樣的數據的方式與功能: readPixels :: Position -> Size -> PixelData a -> IO() 是斷然非Haskelly,因爲它使用的拍攝目標指針和寫入該指針模式C。

第三個參數是重要的部分,它的構造像PixelData PixelFormat DataType (Ptr a)PixelFormatDataType都是枚舉,前者取值如RGBAInteger, RGBA, CMYK, etc,後者取值如UnsignedByte, Short, Float, etc.這些選項讓我感到困惑,但這不是真正的問題。我在哪裏使用指針真的很困難。我不知道什麼樣的Storable數據到malloc來創建指針,或者如果我選擇使用mallocBytes來分配多少字節。

我有一個功能,我瞎搞與目前看起來是這樣的:

pixels :: IO() 
pixels = do 
    pointer <- mallocBytes 50000 
    let pdata = PixelData RGBA Int pointer 
    let pos = Position 0 0 
    let siz = Size 10 10 
    readPixels pos siz pdata 
    print pdata 
    mypixels <- peek pointer 
    -- TODO, what kind of data is mypixels? 
    free pointer 

它運行罰款我只是不知道如何使用我從Ptr獲取數據。也許我不完全理解文檔,但是如何確定指針處的數據類型以及如何在程序中使用它?請注意,我選擇的參數RGBAInt是任意的,它們聽起來足夠無害。我真正想要的是某種格式(如Color 4或某種性質)的RGBA像素值列表或多維列表。任何幫助將不勝感激,我似乎在我的頭上。

回答

7

您正在使用低級別綁定,不要驚訝他們是低級別的。 readPixels直接對應於glReadPixels(它們使用MathML,因此請使用a browser that supports it來查看它)。

你回來的是一個32位整數數組,每一個都包含RGBA顏色值。你只需要width * height * 4字節,你就是過度分配。

FFI機器可以很容易地讀取它 - 它提供peekArray,它可以用來將像素數據提取到Haskell列表中。你也需要使用正確類型的指針。 mallocBytes返回對應於void*的特殊類型的指針。我正在使用ForeignPtr,因此運行時會負責完成它(使用mallocBytes您需要使用castPtr以及bracket來解釋例外情況)。

import Data.Word 
import Foreign 
import Graphics.Rendering.OpenGL 

readPixelArray :: Int -> Int -> Int -> Int -> IO [Word32] 
readPixelArray x y w h = do 
    let arraySize = w * h 
    array <- mallocForeignPtrArray arraySize :: IO (ForeignPtr Word32) 
    withForeignPtr array $ \ptr -> do 
     -- ptr :: Ptr Word32 
     -- fromIntegral is needed because Position and Size store GLints not Ints 
     let position = Position (fromIntegral x) (fromIntegral y) 
     let size = Size (fromIntegral w) (fromIntegral h) 
     readPixels position size $ PixelData RGBA UnsignedInt ptr 
     peekArray arraySize ptr 

現在你已經有了一個一維的顏色值數組。我們可以像下面一起來把它分解到一個類型:

import Data.Bits 

data Pixel = Pixel { 
    red :: Word8, green :: Word8, blue :: Word8, alpha :: Word8 
} deriving (Eq, Show) 

readPixels' :: Int -> Int -> Int -> Int -> IO [Pixel] 
readPixels' x y w h = do 
    rawPixels <- readPixelArray x y w h 
    return $ map pixelFromWord32 rawPixels 

-- pixelFromWord32 0xAABBCCDD = Pixel 0xAA 0xBB 0xCC 0xDD 
pixelFromWord32 :: Word32 -> Pixel 
pixelFromWord32 colour = Pixel (extract 3) (extract 2) (extract 1) (extract 0) 
    where extract idx = fromIntegral $ (colour `shiftR` (idx * 8)) .&. 0xFF 
+0

我得到一個'withForeignPtr'之後的某處有段錯誤,有什麼想法? – Scott

+0

Segfault發生在'peekArray'過程中。 – Scott

+0

固定代碼:https://gist.github.com/ssadler/316daa219ff412e1b5c6 – Scott

1

這裏有一個版本的讀取圖像成JuicyPixels圖像可以很容易地保存爲PNG文件等:

import Codec.Picture (Image, PixelRGB8(..), withImage) 
import Control.Lens.Operators 
import qualified Foreign as F 
import qualified Graphics.Rendering.OpenGL as GL 

takeScreenshot :: IO (Image PixelRGB8) 
takeScreenshot = 
    do 
     (pos, [email protected](GL.Size wGl hGl)) <- GL.get GL.viewport 
     let width = fromIntegral wGl 
     let height = fromIntegral hGl 
     let pixelSize = 3 
     -- glY converts top-origin coordinates to OpenGL's bottom-origin system 
     let glY y = height - 1 - y 
     let pixelOffset x y = ((glY y) * width + x) * pixelSize 
     F.allocaBytes (pixelSize * width * height) $ 
      \ptr -> 
      do 
       GL.readPixels pos size (GL.PixelData GL.RGB GL.UnsignedByte ptr) 
       let readPixel x y = 
         F.plusPtr ptr (pixelOffset x y) 
         & F.peekArray pixelSize 
         <&> (\[r, g, b] -> PixelRGB8 r g b) 
       withImage width height readPixel