8

我有一个使用 OpenGL 和 GLUT 在屏幕上显示一些东西的应用程序。我想访问“逐像素”显示的颜色,但我无法理解所提供的方法。

访问此类数据的方法似乎是使用函数: readPixels :: Position -> Size -> PixelData a -> IO () 这绝对不是 Haskelly,因为它使用 C 模式获取目标指针并写入该指针。

第三个参数是重要的部分,它的构造类似于PixelData PixelFormat DataType (Ptr a)PixelFormat并且DataType都是枚举,前者取值如,RGBAInteger, RGBA, CMYK, etc后者取值如UnsignedByte, Short, Float, etc. The selection 让我感到困惑,但这不是真正的问题。我真的在努力使用指针。我不知道要创建什么样的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 像素值的列表或多维列表。任何帮助将不胜感激,我似乎不知所措。

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

现在你有了一个颜色值的一维数组。我们可以将其拆分为如下类型:

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

这是一个将图像读入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, 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 回答