我有一个使用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.
等值,这些选择让我感到困惑,但这不是真实的问题。我真正在努力使用指针。我不知道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
和Int
是任意的,它们只是听起来无害。我真正想要的是某种格式的一些列表或RGBA像素值的多维列表(Color 4
或那种性质的东西)。任何帮助都将非常感激,我似乎在我的脑海里。
答案 0 :(得分: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
答案 1 :(得分: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