File: ReadImage.hs

package info (click to toggle)
haskell-glut 2.4.0.0-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 1,624 kB
  • ctags: 28
  • sloc: haskell: 10,610; ansic: 121; makefile: 2
file content (65 lines) | stat: -rw-r--r-- 2,514 bytes parent folder | download | duplicates (9)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
{-
   ReadImage.hs (adapted from readImage.c which is (c) Silicon Graphics, Inc.)
   Copyright (c) Sven Panne 2002-2005 <sven.panne@aedion.de>
   This file is part of HOpenGL and distributed under a BSD-style license
   See the file libraries/GLUT/LICENSE

   Support for reading a file of raw RGB data:
      4 bytes big-endian width
      4 bytes big-endian height
      width * height RGB byte triples
-}

module ReadImage ( readImage ) where

import Data.Word ( Word8, Word32 )
import Control.Exception ( bracket )
import Control.Monad ( when )
import System.IO ( Handle, IOMode(ReadMode), openBinaryFile, hGetBuf, hClose )
import System.IO.Error ( mkIOError, eofErrorType )
import Foreign ( Ptr, alloca, mallocBytes, Storable(..) )
import Graphics.UI.GLUT

-- This is probably overkill, but anyway...
newtype Word32BigEndian = Word32BigEndian Word32

word32BigEndianToGLsizei :: Word32BigEndian -> GLsizei
word32BigEndianToGLsizei (Word32BigEndian x) = fromIntegral x

instance Storable Word32BigEndian where
   sizeOf ~(Word32BigEndian x) = sizeOf x
   alignment ~(Word32BigEndian x) = alignment x
   peek ptr = do
      let numBytes = sizeOf (undefined :: Word32BigEndian)
      bytes <- mapM (peekByteOff ptr) [ 0 ..  numBytes - 1 ] :: IO [Word8]
      let value = foldl (\val byte -> val * 256 + fromIntegral byte) 0 bytes
      return $ Word32BigEndian value
   poke = error ""

-- This is the reason for all this stuff above...
readGLsizei :: Handle -> IO GLsizei
readGLsizei handle =
   alloca $ \buf -> do
      hGetBufFully handle buf (sizeOf (undefined :: Word32BigEndian))
      fmap word32BigEndianToGLsizei $ peek buf

-- A handy variant of hGetBuf with additional error checking
hGetBufFully :: Handle -> Ptr a -> Int -> IO ()
hGetBufFully handle ptr numBytes = do
   bytesRead <- hGetBuf handle ptr numBytes
   when (bytesRead /= numBytes) $
      ioError $ mkIOError eofErrorType "hGetBufFully" (Just handle) Nothing

-- Closing a file is nice, even when an error occurs during reading.
withBinaryFile :: FilePath -> (Handle -> IO a) -> IO a
withBinaryFile filePath = bracket (openBinaryFile filePath ReadMode) hClose

readImage :: FilePath -> IO (Size, PixelData a)
readImage filePath =
   withBinaryFile filePath $ \handle -> do
      width <- readGLsizei handle
      height <- readGLsizei handle
      let numBytes = fromIntegral (3 * width * height)
      buf <- mallocBytes numBytes
      hGetBufFully handle buf numBytes
      return (Size width height, PixelData RGB UnsignedByte buf)