8

OpenGL と GLUT を使用して画面に表示するアプリケーションがあります。「ピクセルごと」に表示された色にアクセスしたいのですが、提供されている方法を理解するのに苦労しています。

そのようなデータにアクセスする方法は、 function: を使用しているようです。readPixels :: Position -> Size -> PixelData a -> IO () これは、宛先ポインターを取得してそのポインターに書き込む C パターンを使用するため、明らかに非 Haskelly です。

3 番目の引数は重要な部分であり、 のように構成されてい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。ドキュメントを完全に理解していない可能性がありますが、ポインターのデータの型をどのように判断し、プログラムでどのように使用できますか? 私の引数の選択は恣意的であり、無害に聞こえただけであることに注意してRGBAくださいIntColor 4私が本当に欲しいのは、何らかの形式 (またはその性質のもの)の RGBA ピクセル値のリストまたは多次元リストです。どんな助けでも大歓迎です、私は頭を抱えているようです。

4

2 に答える 2

7

低レベルのバインディングを使用していますが、それらが低レベルであることに驚かないでください。readPixelsに直接対応しglReadPixelsます(MathML を使用しているため、表示には対応したブラウザを使用してください)。

返されるのは、32 ビット整数の配列で、それぞれに RGBA カラー値が含まれています。そのために必要なのはwidth * height * 4バイトだけです。多くの割り当てを超過しています。

それからの読み取りは、FFI 機構によって非常に簡単になりますpeekArray。これは、ピクセル データを Haskell リストに抽出するために使用できるものを提供します。正しいタイプのポインターも使用する必要があります。mallocBytesに対応する特別な型のポインタを返しますvoid*ForeignPtrランタイムがそれをファイナライズするように使用しています(使用mallocBytesする必要がありcastPtrbracket例外を説明する必要があります)。

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

これで、色値の 1 次元配列が得られました。次のようなタイプに分割できます。

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
于 2013-01-09T13:16:32.847 に答える
1

これは、画像を PNG ファイルなどに簡単に保存できるJuicyPixels画像に読み込むバージョンです。

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, size@(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
于 2016-04-05T11:49:46.550 に答える