diff --git a/Data/Array/Repa/IO/BMP.hs b/Data/Array/Repa/IO/BMP.hs
new file mode 100644
--- /dev/null
+++ b/Data/Array/Repa/IO/BMP.hs
@@ -0,0 +1,186 @@
+{-# LANGUAGE PackageImports #-} 
+
+-- | Reading and writing arrays as uncompressed 24 and 32 bit Windows BMP files.
+module Data.Array.Repa.IO.BMP
+	( readImageFromBMP
+	, readComponentsFromBMP
+	, readMatrixFromGreyscaleBMP
+	, writeImageToBMP
+	, writeComponentsToBMP
+	, writeMatrixToGreyscaleBMP)
+where
+import qualified  Data.Array.Parallel.Unlifted 	as U
+import Data.Array.Repa				as A
+import Data.Array.Repa.ByteString               as A
+import Prelude					as P
+import Codec.BMP
+import Data.Word
+
+-- Read -------------------------------------------------------------------------------------------
+-- | Read a matrix from a `BMP` file.
+--	Each pixel is converted to greyscale, normalised to [0..1] and used
+--	as the corresponding array element. If anything goes wrong when loading the file then `Error`.
+readMatrixFromGreyscaleBMP
+	:: FilePath
+	-> IO (Either Error (Array DIM2 Double))
+
+readMatrixFromGreyscaleBMP filePath
+ = do	eComps	<- readComponentsFromBMP filePath
+	case eComps of 
+	 Left err	-> return $ Left err
+  	 Right (arrRed, arrGreen, arrBlue)
+ 	  -> let arr	= force 
+			$ A.fromFunction (extent arrRed)
+			   (\ix -> sqrt ( (fromIntegral (arrRed   !: ix) / 255) ^ (2 :: Int)
+					+ (fromIntegral (arrGreen !: ix) / 255) ^ (2 :: Int)
+					+ (fromIntegral (arrBlue  !: ix) / 255) ^ (2 :: Int)))
+	     in	arr `deepSeqArray` return (Right arr)
+		
+
+-- | Read RGB components from a BMP file.
+--	Returns arrays of red, green and blue components, all with the same extent.
+--	If anything goes wrong when loading the file then then `Error`.
+readComponentsFromBMP
+	:: FilePath
+	-> IO (Either Error (Array DIM2 Word8, Array DIM2 Word8, Array DIM2 Word8))
+
+{-# INLINE readComponentsFromBMP #-}
+readComponentsFromBMP filePath
+ = do	ebmp	<- readBMP filePath
+	case ebmp of
+	 Left err	-> return $ Left err
+	 Right bmp	-> return $ Right (readComponentsFromBMP' bmp)
+
+readComponentsFromBMP' bmp
+ = let	(width, height) = bmpDimensions bmp
+
+	arr		= A.fromByteString (Z :. height :. width * 4)
+			$ unpackBMPToRGBA32 bmp
+
+	shapeFn _ 	= Z :. height :. width
+
+	arrRed	
+	 = traverse arr shapeFn
+		(\get (sh :. x) -> get (sh :. (x * 4)))
+
+	arrGreen
+	 = traverse arr shapeFn
+		(\get (sh :. x) -> get (sh :. (x * 4 + 1)))
+
+	arrBlue
+	 = traverse arr shapeFn
+		(\get (sh :. x) -> get (sh :. (x * 4 + 2)))
+	
+   in	(arrRed, arrGreen, arrBlue)
+
+
+-- | Read a RGBA image from a BMP file.
+--	In the result, the higher two dimensions are the height and width,
+--	and the lower indexes the RGBA component of each pixel. 
+--      If the BMP read has no alpha channel then alpha of the resulting pixels is set to 255.
+--	If anything goes wrong when loading the file then `Error`.
+readImageFromBMP 
+	:: FilePath
+	-> IO (Either Error (Array DIM3 Word8))
+
+readImageFromBMP filePath
+ = do	ebmp	<- readBMP filePath
+	case ebmp of
+	 Left err	-> return $ Left err
+	 Right bmp	-> return $ Right (readImageFromBMP' bmp)
+	
+readImageFromBMP' bmp
+ = let	(width, height)	= bmpDimensions bmp
+	arr		= fromByteString (Z :. height :. width :. 4)
+			$ unpackBMPToRGBA32 bmp
+   in	arr
+
+
+
+-- Write ------------------------------------------------------------------------------------------
+-- | Write a matrix to a BMP file.
+--	Negative values are discarded. Positive values are normalised to the maximum 
+--	value in the matrix and used as greyscale pixels.
+writeMatrixToGreyscaleBMP 
+	:: FilePath
+	-> Array DIM2 Double
+	-> IO ()
+
+writeMatrixToGreyscaleBMP fileName arr
+ = let	arrNorm		= normalisePositive01 arr
+
+	scale :: Double -> Word8
+	scale x		= fromIntegral (truncate (x * 255) :: Int)
+
+	arrWord8	= A.map scale arrNorm
+   in	writeComponentsToBMP fileName arrWord8 arrWord8 arrWord8
+		
+
+-- | Write RGB components to a BMP file.
+--	All arrays must have the same extent, else `error`.
+writeComponentsToBMP
+	:: FilePath
+	-> Array DIM2 Word8
+	-> Array DIM2 Word8
+	-> Array DIM2 Word8
+	-> IO ()
+
+writeComponentsToBMP fileName arrRed arrGreen arrBlue
+ | not $ (  extent arrRed   == extent arrGreen       
+         && extent arrGreen == extent arrBlue)
+ = error "Data.Array.Repa.IO.BMP.writeComponentsToBMP: arrays don't have same extent"
+
+ | otherwise
+ = do	let Z :. height :. width	
+			= extent arrRed
+		
+	-- Build image data from the arrays.
+	let arrAlpha	= fromFunction (extent arrRed) (\_ -> 255)
+	let arrRGBA	= interleave4 arrRed arrGreen arrBlue arrAlpha
+	let bmp		= packRGBA32ToBMP width height
+			$ A.toByteString arrRGBA
+	
+	writeBMP fileName bmp
+
+
+-- | Write a RGBA image to a BMP file.
+--	The higher two dimensions are the height and width of the image. 
+--	The lowest dimension must have size 4, corresponding to the RGBA components
+--	of each pixel, else `error`. 
+writeImageToBMP 
+	:: FilePath
+	-> Array DIM3 Word8
+	-> IO ()
+
+writeImageToBMP fileName arrImage
+	| comps /= 4
+	= error "Data.Array.Repa.IO.BMP: lowest order dimension must be 4"
+
+	| otherwise
+	= let 	bmp	= packRGBA32ToBMP height width 
+			$ A.toByteString arrImage
+	  in	writeBMP fileName bmp
+	
+	where	Z :. height :. width :. comps	
+			= extent arrImage
+	
+
+-- Normalise --------------------------------------------------------------------------------------
+-- | Normalise a matrix to to [0 .. 1], discarding negative values.
+--	If the maximum value is 0 then return the array unchanged.
+normalisePositive01
+	:: (Shape sh, U.Elt a, Fractional a, Ord a)
+	=> Array sh a
+	-> Array sh a
+
+{-# INLINE normalisePositive01 #-}
+normalisePositive01 arr	
+ = let	mx		= foldAll max 0 arr
+   	elemFn x
+	 | x >= 0	= x / mx
+	 | otherwise	= x
+   in	mx `seq`
+	 if mx == 0 
+	  then arr
+	  else A.map elemFn arr
+
diff --git a/Data/Array/Repa/IO/ColorRamp.hs b/Data/Array/Repa/IO/ColorRamp.hs
new file mode 100644
--- /dev/null
+++ b/Data/Array/Repa/IO/ColorRamp.hs
@@ -0,0 +1,48 @@
+{-# LANGUAGE RankNTypes #-}
+
+-- | Hyprometric color ramps, for making pretty images from scalar data.
+module	Data.Array.Repa.IO.ColorRamp
+	(rampColorHotToCold)
+where
+
+-- | Standard Hot to Cold hypsometric color ramp.
+--	Color sequence is red, yellow, green, cyan, blue.
+rampColorHotToCold 
+	:: forall a
+	.  (Ord a, Floating a) 
+	=> a 	-- ^ Minimum value of range.
+	-> a 	-- ^ Maximum value of range.
+	-> a 	-- ^ Data value.
+	-> (a, a, a)
+	
+{-# INLINE rampColorHotToCold #-}
+rampColorHotToCold vmin vmax vNotNorm
+ = let	
+	v	| vNotNorm < vmin	= vmin
+	 	| vNotNorm > vmax	= vmax
+		| otherwise		= vNotNorm
+	
+	dv	= vmax - vmin	
+
+	result	| v < vmin + 0.25 * dv
+		= ( 0
+		  , 4 * (v - vmin) / dv
+		  , 1.0)
+		
+		| v < vmin + 0.5 * dv
+		= ( 0
+		  , 1.0
+		  , 1 + 4 * (vmin + 0.25 * dv - v) / dv)
+		
+		| v < vmin + 0.75 * dv
+		= ( 4 * (v - vmin - 0.5 * dv) / dv
+		  , 1.0
+		  , 0.0)
+		
+		| otherwise
+		= ( 1.0
+		  , 1 + 4 * (vmin + 0.75 * dv - v) / dv
+		  , 0)
+		
+  in	result
+
diff --git a/Data/Array/Repa/IO/Internals/Text.hs b/Data/Array/Repa/IO/Internals/Text.hs
new file mode 100644
--- /dev/null
+++ b/Data/Array/Repa/IO/Internals/Text.hs
@@ -0,0 +1,41 @@
+{-# OPTIONS_HADDOCK hide #-}
+module Data.Array.Repa.IO.Internals.Text
+	( hWriteValues
+	, readValues)
+where
+import System.IO
+import Data.Char
+
+-- Stuff shared with Matrix module -------------------------------------------------------------
+-- | Write out values to a file.
+hWriteValues
+	:: Show a
+	=> Handle 
+	-> [a] 				-- ^ Data values.
+	-> IO ()
+
+hWriteValues handle xx
+ = go xx
+ where	go []		= return ()
+	go (x:xs)
+	 = do	hPutStr handle $ show x
+		hPutStr handle $ "\n"
+		go xs
+
+
+-- | Read a string containing ints separated by whitespace.	
+readValues :: (Num a, Read a) => String -> [a]
+readValues cs	= readValues' [] cs
+ where	readValues' _ []	= []
+	readValues' acc (c : rest)
+		| isSpace c
+		= if null acc 
+			then readValues' [] rest
+			else read (reverse acc) : readValues' [] rest
+
+		| isDigit c || c == '.' || c == 'e' || c == '-'
+		= readValues' (c : acc) rest
+
+		| otherwise
+		= error $ "unexpected char in Matrix file " ++ show (ord c)
+
diff --git a/Data/Array/Repa/IO/Matrix.hs b/Data/Array/Repa/IO/Matrix.hs
new file mode 100644
--- /dev/null
+++ b/Data/Array/Repa/IO/Matrix.hs
@@ -0,0 +1,67 @@
+{-# LANGUAGE PackageImports #-}
+-- | Read and write matrices as ASCII text files.
+--
+--   The file format is like:
+--
+--   @
+--	MATRIX			-- header
+--	100 100			-- width and height
+--	1.23 1.56 1.23 ...	-- data, separated by whitespace
+--	....
+--   @
+module Data.Array.Repa.IO.Matrix
+	( readMatrixFromTextFile
+	, writeMatrixToTextFile)
+where
+import Data.Array.Repa.IO.Internals.Text
+import Control.Monad
+import System.IO
+import Data.List				as L
+import Data.Array.Repa				as A
+import Prelude					as P
+import qualified "dph-prim-par" Data.Array.Parallel.Unlifted	as U
+
+
+-- | Read a matrix from a text file.
+--
+--   WARNING: This doesn't do graceful error handling. If the file has the wrong format
+--   you'll get a confusing `error`.
+readMatrixFromTextFile
+	:: (U.Elt a, Num a, Read a)
+	=> FilePath
+	-> IO (Array DIM2 a)	
+
+readMatrixFromTextFile fileName
+ = do	handle		<- openFile fileName ReadMode
+	
+	"MATRIX"	<- hGetLine handle
+	[width, height]	<- liftM (P.map read . words) $ hGetLine handle
+	str		<- hGetContents handle
+	let vals	= readValues str
+
+	let dims	= Z :. width :. height
+	let mat		= fromList dims vals
+
+	return mat
+
+
+-- | Write a matrix as a text file.
+writeMatrixToTextFile 
+	:: (U.Elt a, Show a)
+	=> FilePath
+	-> Array DIM2 a
+	-> IO ()
+
+writeMatrixToTextFile fileName arr
+ = do	file	<- openFile fileName WriteMode	
+
+	hPutStrLn file "MATRIX"
+
+	let Z :. width :. height	
+		= extent arr
+
+	hPutStrLn file $ show width ++ " " ++ show height
+		
+	hWriteValues file $ toList arr
+	hClose file
+
diff --git a/Data/Array/Repa/IO/Vector.hs b/Data/Array/Repa/IO/Vector.hs
new file mode 100644
--- /dev/null
+++ b/Data/Array/Repa/IO/Vector.hs
@@ -0,0 +1,75 @@
+{-# LANGUAGE PackageImports #-}
+-- | Read and write vectors as ASCII text files.
+--
+--   The file format is like:
+--
+--   @
+--	VECTOR			-- header
+--	100			-- length of vector
+--	1.23 1.56 1.23 ...	-- data, separated by whitespace
+--	....
+--   @
+module Data.Array.Repa.IO.Vector
+	( readVectorFromTextFile
+	, writeVectorToTextFile)
+where
+import Data.Array.Repa				as A
+import Data.Array.Repa.IO.Internals.Text
+import Data.List				as L
+import Prelude					as P
+import System.IO
+import Control.Monad
+import Data.Char
+import qualified "dph-prim-par" Data.Array.Parallel.Unlifted	as U
+
+
+-- | Read a vector from a text file.
+--   WARNING: This doesn't do graceful error handling. If the file has the wrong format
+--   you'll get a confusing `error`.
+readVectorFromTextFile
+	:: (U.Elt a, Num a, Read a)
+	=> FilePath
+	-> IO (Array DIM1 a)	
+
+readVectorFromTextFile fileName
+ = do	handle		<- openFile fileName ReadMode
+	
+	"VECTOR"	<- hGetLine handle
+	[len]		<- liftM (P.map readInt . words) $ hGetLine handle
+	str		<- hGetContents handle
+	let vals	= readValues str
+
+	let dims	= Z :. len
+	let vec		= fromList dims vals
+
+	return vec
+
+readInt :: String -> Int
+readInt str
+	| and $ P.map isDigit str
+	= read str
+	
+	| otherwise
+	= error "Data.Array.Repa.IO.Vector.readVectorFromTextFile parse error when reading data"
+	
+	
+-- | Write a vector as a text file.
+writeVectorToTextFile 
+	:: (U.Elt a, Show a)
+	=> Array DIM1 a
+	-> FilePath
+	-> IO ()
+
+writeVectorToTextFile arr fileName
+ = do	file	<- openFile fileName WriteMode	
+
+	hPutStrLn file "VECTOR"
+
+	let Z :. len
+		= extent arr
+
+	hPutStrLn file 	$ show len
+	hWriteValues file $ toList arr
+	hClose file
+	hFlush file
+	
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2010, University of New South Wales.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of the University of New South Wales nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/repa-io.cabal b/repa-io.cabal
new file mode 100644
--- /dev/null
+++ b/repa-io.cabal
@@ -0,0 +1,40 @@
+Name:                repa-io
+Version:             1.1.0.0
+License:             BSD3
+License-file:        LICENSE
+Author:              The DPH Team
+Maintainer:          Ben Lippmeier <benl@ouroborus.net>
+Build-Type:          Simple
+Cabal-Version:       >=1.6
+Stability:           experimental
+Category:            Data Structures
+Homepage:            http://trac.haskell.org/repa
+Bug-reports:         http://trac.haskell.org/repa/newticket
+Description:
+        NOTE: You must use the GHC head branch > 6.13.20100309 to get decent performance.
+        Read and write Repa arrays in various formats.
+
+Synopsis:
+        Read and write Repa arrays in various formats.
+
+Tested-with: GHC == 6.13.20100309, GHC == 6.12.1
+
+Library
+  Build-Depends: 
+        base                 == 4.*,
+        dph-prim-par         == 0.4.*,
+        repa                 == 1.1.*,
+        repa-bytestring      == 1.1.*,
+        bmp                  == 1.1.*
+
+  ghc-options:
+        -Odph -Wall -fno-warn-missing-signatures
+
+  Exposed-modules:
+        Data.Array.Repa.IO.ColorRamp
+        Data.Array.Repa.IO.BMP
+        Data.Array.Repa.IO.Vector
+        Data.Array.Repa.IO.Matrix
+      
+  Other-modules:
+        Data.Array.Repa.IO.Internals.Text
