2013-01-09 62 views
8

Tôi có một ứng dụng sử dụng OpenGL và GLUT để hiển thị một số nội dung trên màn hình. Tôi muốn truy cập các màu được hiển thị "pixel by pixel" nhưng tôi gặp sự cố khi hiểu các phương thức được cung cấp.Đọc pixel từ Ứng dụng OpenGL/GLUT

Dường như cách truy cập dữ liệu như vậy là có chức năng: readPixels :: Position -> Size -> PixelData a -> IO() quyết định không Haskelly vì sử dụng mẫu C để lấy con trỏ đích và ghi vào con trỏ đó.

Đối số thứ ba là phần quan trọng và được xây dựng như PixelData PixelFormat DataType (Ptr a). PixelFormatDataType là cả hai enums, các giá trị cũ tham gia như RGBAInteger, RGBA, CMYK, etc và sau này lấy giá trị như UnsignedByte, Short, Float, etc. Các lựa chọn là khó hiểu với tôi, nhưng đó không phải là vấn đề thực sự. Nơi tôi thực sự đấu tranh với việc sử dụng con trỏ. Tôi không biết loại dữ liệu Storable nào là malloc để tạo con trỏ hoặc có bao nhiêu byte để phân bổ nếu tôi chọn sử dụng mallocBytes.

Tôi có một chức năng tôi đang rối tung xung quanh với hiện trông giống như sau:

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 

Nó chạy tốt tôi chỉ không có ý tưởng làm thế nào để sử dụng dữ liệu tôi nhận được từ Ptr. Có lẽ tôi không hiểu đầy đủ về tài liệu, nhưng làm cách nào tôi có thể xác định loại dữ liệu tại con trỏ và cách tôi có thể sử dụng nó trong chương trình của mình? Lưu ý rằng sự lựa chọn của tôi đối số RGBAInt là tùy ý, chúng chỉ nghe đủ vô hại. Những gì tôi thực sự muốn là một số danh sách hoặc danh sách đa chiều của các giá trị pixel RGBA ở một số định dạng (Color 4 hoặc một cái gì đó có tính chất đó). Bất kỳ sự giúp đỡ nào sẽ được đánh giá rất cao, tôi dường như đang ở trong đầu tôi.

Trả lời

7

Bạn đang sử dụng các liên kết cấp thấp, đừng ngạc nhiên khi chúng ở mức độ thấp. readPixels tương ứng trực tiếp với glReadPixels (chúng sử dụng MathML, vì vậy hãy sử dụng a browser that supports it để xem nó).

Điều bạn đang quay lại là một mảng các số nguyên 32 bit, mỗi số chứa giá trị màu RGBA. Bạn chỉ cần width * height * 4 byte cho điều đó, bạn đang phân bổ tổng thể rất nhiều.

Đọc từ nó được thực hiện khá dễ dàng bằng máy FFI - nó cung cấp peekArray có thể được sử dụng để trích xuất dữ liệu pixel vào danh sách Haskell. Bạn cần phải làm việc với một con trỏ của loại chính xác, quá. mallocBytes trả về con trỏ của loại đặc biệt tương ứng với void*. Tôi đang sử dụng ForeignPtr để thời gian chạy có nhiệm vụ hoàn thành nó (với mallocBytes bạn cần sử dụng castPtr và cũng bracket để giải thích ngoại lệ).

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 

Bây giờ bạn đã có một mảng giá trị màu một chiều. Chúng tôi có thể chia nhỏ nó lên đến một loại như thế này:

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

tôi nhận được một segfault một nơi nào đó sau khi 'withForeignPtr', bất kỳ ý tưởng? – Scott

+0

Segfault xảy ra trong 'peekArray'. – Scott

+0

Mã cố định: https://gist.github.com/ssadler/316daa219ff412e1b5c6 – Scott

1

Dưới đây là một phiên bản mà đọc hình ảnh thành một hình ảnh JuicyPixels có thể dễ dàng lưu vào một tập tin PNG vv:

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 
Các vấn đề liên quan