Home My Page Projects Code Snippets Project Openings 3D graphics for Standard ML
Summary Activity SCM

SCM Repository

[sml3d] Annotation of /trunk/sml3d/src/image-io/tga-file-io.sml
ViewVC logotype

Annotation of /trunk/sml3d/src/image-io/tga-file-io.sml

Parent Directory Parent Directory | Revision Log Revision Log


Revision 274 - (view) (download)
Original Path: src/image/tga-io.sml

1 : jhr 274 (* tga-io.sml
2 :     *
3 :     * COPYRIGHT (c) 2008 John Reppy (http://cs.uchicago.edu/~jhr)
4 :     * All rights reserved.
5 :     *)
6 :    
7 : jhr 273 structure TGAIO : sig
8 :    
9 :     (* the native formats that a TGA file can hold (at least those that
10 :     * we handle!)
11 :     *)
12 : jhr 274 datatype tga_image
13 :     = Gray of Word8.word ImageTypes.image2
14 :     | GrayAlpha of (Word8.word * Word8.word) ImageTypes.image2
15 :     | RGB of (Word8.word * Word8.word * Word8.word) ImageTypes.image2
16 :     | RGBA of (Word8.word * Word8.word * Word8.word * Word8.word) ImageTypes.image2
17 : jhr 273
18 : jhr 274 val loadFile : {file : string, flip : bool} -> tga_image
19 : jhr 273
20 :     end = struct
21 :    
22 :     structure W8V = Word8Vector
23 :    
24 : jhr 274 (* need to do something sensible about error reporting *)
25 :     fun error msg = raise Fail msg
26 :    
27 : jhr 273 (* the native formats that a TGA file can hold (at least those that
28 :     * we handle!)
29 :     *)
30 : jhr 274 datatype tga_image
31 :     = Gray of Word8.word ImageTypes.image2
32 :     | GrayAlpha of (Word8.word * Word8.word) ImageTypes.image2
33 :     | RGB of (Word8.word * Word8.word * Word8.word) ImageTypes.image2
34 :     | RGBA of (Word8.word * Word8.word * Word8.word * Word8.word) ImageTypes.image2
35 : jhr 273
36 :     (* TGA file-header layout *)
37 :     val hdrSz = 18
38 :     val offIDLength = 0
39 :     val offCMapType = 1
40 :     val offImageType = 2
41 :     val offCMapSpec = 3
42 :     val offWid = 12
43 :     val offHt = 14
44 :     val offBPP = 16
45 :     val offImgDesc = 17
46 :    
47 : jhr 274 fun getByte (vec, i) = Word.fromLargeWord(Word8.toLargeWordX(W8V.sub(vec, i)))
48 :     fun getShort (vec, i) = Word.toIntX(Word.orb(
49 : jhr 273 Word.<< (getByte(vec, i+1), 0w8),
50 :     getByte(vec, i)))
51 :    
52 :     (* read the file header *)
53 :     fun readHdr inS = let
54 :     val vec = BinIO.inputN (inS, hdrSz)
55 :     in
56 :     if (W8V.length vec <> hdrSz)
57 : jhr 274 then error "bogus TGA file"
58 : jhr 273 else let
59 :     val idLen = getByte(vec, offIDLength)
60 :     val cmapTy = getByte(vec, offCMapType)
61 : jhr 274 val (color, rle) = (case getByte(vec, offImageType)
62 :     of 0w2 => (true, false)
63 :     | 0w3 => (false, false)
64 :     | 0w10 => (true, true)
65 :     | 0w11 => (false, true)
66 :     | n => error "unsupported image type"
67 : jhr 273 (* end case *))
68 :     val wid = getShort(vec, offWid)
69 :     val ht = getShort(vec, offHt)
70 : jhr 274 val bytesPP = Word.toIntX(getByte(vec, offBPP) div 0w8)
71 : jhr 273 val imageDesc = getByte(vec, offImgDesc)
72 : jhr 274 val alpha = (Word.andb(imageDesc, 0wxF) <> 0w0)
73 : jhr 273 in
74 :     (* do some sanity checking *)
75 : jhr 274 if (cmapTy <> 0w0) then error "ill-formed TGA header" else ();
76 : jhr 273 case (color, alpha, bytesPP)
77 :     of (true, true, 4) => () (* RGBA data *)
78 :     | (true, false, 3) => () (* RGB data *)
79 :     | (false, false, 1) => () (* 8-bit gray-scale *)
80 : jhr 274 | _ => error "unsupported format"
81 : jhr 273 (* end case *);
82 :     (* skip the ID field, if present *)
83 :     if (idLen <> 0w0)
84 : jhr 274 then ignore(BinIO.inputN(inS, Word.toIntX idLen))
85 : jhr 273 else ();
86 :     (* return the header information *)
87 :     { wid = wid,
88 :     ht = ht,
89 :     bytesPerPixel = bytesPP,
90 : jhr 274 size = wid * ht * bytesPP,
91 : jhr 273 compressed = rle
92 :     }
93 :     end
94 :     end
95 :    
96 :     (* parse a packet header in a RLE image *)
97 :     fun parsePacketHdr ub = let
98 : jhr 274 val np = Word8.andb(ub, 0wx7f)
99 : jhr 273 in
100 :     {raw = (ub = np), nPixels = Word8.toInt np + 1}
101 :     end
102 :    
103 : jhr 274 local
104 :     fun getGrayPixel (v, i) = W8V.sub(v, i)
105 :     fun getGrayAlphaPixel (v, i) = (W8V.sub(v, i), W8V.sub(v, i+1))
106 :     fun getRGBPixel (v, i) = (W8V.sub(v, i), W8V.sub(v, i+1), W8V.sub(v, i+2))
107 :     fun getRGBAPixel (v, i) = (W8V.sub(v, i), W8V.sub(v, i+1), W8V.sub(v, i+2), W8V.sub(v, i+3))
108 :     fun readRow (bpp, get, put) (inS, buf, i, wid) = let
109 :     val n = bpp*wid
110 :     val v = BinIO.inputN (inS, n)
111 :     fun lp j = if (j < wid)
112 :     then (put(buf, i+j, get(v, j)); lp j)
113 :     else i+wid
114 :     in
115 :     if (W8V.length v <> n)
116 :     then error "error reading file"
117 :     else lp 0
118 :     end
119 :     in
120 :     val readGrayRow = readRow (1, getGrayPixel, DataBuffer.setub)
121 :     val readGrayAlphaRow = readRow (2, getGrayAlphaPixel, DataBuffer.set2ub)
122 :     val readRGBRow = readRow (3, getRGBPixel, DataBuffer.set3ub)
123 :     val readRGBARow = readRow (3, getRGBAPixel, DataBuffer.set4ub)
124 :     end
125 : jhr 273
126 : jhr 274 local
127 :     fun getGrayPixel inS = valOf(BinIO.input1 inS)
128 :     fun getGrayAlphaPixel inS = let
129 :     val v = BinIO.inputN(inS, 2)
130 :     in
131 :     (W8V.sub(v, 0), W8V.sub(v, 1))
132 :     end
133 :     fun getRGBPixel inS = let
134 :     val v = BinIO.inputN(inS, 3)
135 :     in
136 :     (W8V.sub(v, 0), W8V.sub(v, 1), W8V.sub(v, 2))
137 :     end
138 :     fun getRGBAPixel inS = let
139 :     val v = BinIO.inputN(inS, 4)
140 :     in
141 :     (W8V.sub(v, 0), W8V.sub(v, 1), W8V.sub(v, 2), W8V.sub(v, 3))
142 :     end
143 :    
144 :     (* read and uncompress a row of an REL encoded image *)
145 :     fun readRLERow (get, put) (inS, buf, i, wid) = let
146 :     val stop = i + wid
147 :     fun lp i = if (i < stop)
148 :     then let
149 :     val {raw, nPixels} = parsePacketHdr(valOf(BinIO.input1 inS))
150 :     in
151 :     if raw
152 :     then let
153 :     fun read (i, 0) = lp i
154 :     | read (i, n) = (put(buf, i, get inS); read(i+1, n-1))
155 :     in
156 :     read (i, nPixels)
157 :     end
158 :     else let
159 :     val pix = get inS
160 :     fun repeat (i, 0) = lp i
161 :     | repeat (i, n) = (put(buf, i, pix); repeat(i+1, n-1))
162 :     in
163 :     repeat (i, nPixels)
164 :     end
165 :     end
166 :     else i
167 :     in
168 :     lp i
169 :     end
170 :     in
171 : jhr 273 val readGrayRLERow = readRLERow (getGrayPixel, DataBuffer.setub)
172 :     val readGrayAlphaRLERow = readRLERow (getGrayAlphaPixel, DataBuffer.set2ub)
173 :     val readRGBRLERow = readRLERow (getRGBPixel, DataBuffer.set3ub)
174 :     val readRGBARLERow = readRLERow (getRGBAPixel, DataBuffer.set4ub)
175 : jhr 274 end
176 : jhr 273
177 :     (* read an image while row by row *)
178 :     fun readImage readRow (inS, wid, ht, buf) = let
179 :     val stop = wid * ht
180 :     fun lp i = if (i < stop)
181 :     then lp (readRow (inS, buf, i, wid))
182 :     else ()
183 :     in
184 :     lp 0
185 :     end
186 :    
187 :     (* read an image while flipping the Y axis *)
188 :     fun flipImage readRow (inS, wid, ht, buf) = let
189 :     val start = (ht - 1) * wid
190 :     fun lp i = if (i < 0)
191 :     then ()
192 :     else (ignore (readRow (inS, buf, i, wid)); lp(i-wid))
193 :     in
194 :     lp ((ht - 1) * wid)
195 :     end
196 :    
197 :     fun loadFile {file, flip} = let
198 :     val inS = BinIO.openIn file
199 :     fun load () = let
200 : jhr 274 val {wid, ht, size, bytesPerPixel, compressed} = readHdr inS
201 : jhr 273 in
202 : jhr 274 case bytesPerPixel
203 :     of 1 => let
204 :     val buf = DataBuffer.new (DataBuffer.sizeub, size)
205 :     in
206 :     case (flip, compressed)
207 :     of (false, false) => readImage readGrayRow (inS, wid, ht, buf)
208 :     | (false, true) => readImage readGrayRLERow (inS, wid, ht, buf)
209 :     | (true, false) => flipImage readGrayRow (inS, wid, ht, buf)
210 :     | (true, true) => flipImage readGrayRLERow (inS, wid, ht, buf)
211 :     (* end case *);
212 :     Gray{wid=wid, ht=ht, data=buf}
213 :     end
214 :     | 2 => let
215 :     val buf = DataBuffer.new (DataBuffer.size2ub, size)
216 :     in
217 :     case (flip, compressed)
218 :     of (false, false) => readImage readGrayAlphaRow (inS, wid, ht, buf)
219 :     | (false, true) => readImage readGrayAlphaRLERow (inS, wid, ht, buf)
220 :     | (true, false) => flipImage readGrayAlphaRow (inS, wid, ht, buf)
221 :     | (true, true) => flipImage readGrayAlphaRLERow (inS, wid, ht, buf)
222 :     (* end case *);
223 :     GrayAlpha{wid=wid, ht=ht, data=buf}
224 :     end
225 :     | 3 => let
226 :     val buf = DataBuffer.new (DataBuffer.size3ub, size)
227 :     in
228 :     case (flip, compressed)
229 :     of (false, false) => readImage readRGBRow (inS, wid, ht, buf)
230 :     | (false, true) => readImage readRGBRLERow (inS, wid, ht, buf)
231 :     | (true, false) => flipImage readRGBRow (inS, wid, ht, buf)
232 :     | (true, true) => flipImage readRGBRLERow (inS, wid, ht, buf)
233 :     (* end case *);
234 :     RGB{wid=wid, ht=ht, data=buf}
235 :     end
236 :     | 4 => let
237 :     val buf = DataBuffer.new (DataBuffer.size4ub, size)
238 :     in
239 :     case (flip, compressed)
240 :     of (false, false) => readImage readRGBARow (inS, wid, ht, buf)
241 :     | (false, true) => readImage readRGBARLERow (inS, wid, ht, buf)
242 :     | (true, false) => flipImage readRGBARow (inS, wid, ht, buf)
243 :     | (true, true) => flipImage readRGBARLERow (inS, wid, ht, buf)
244 :     (* end case *);
245 :     RGBA{wid=wid, ht=ht, data=buf}
246 :     end
247 :     (* end case *)
248 : jhr 273 end
249 :     in
250 :     load () handle ex => (BinIO.closeIn inS; raise ex)
251 :     end
252 :    
253 :     end
254 :    

root@smlnj-gforge.cs.uchicago.edu
ViewVC Help
Powered by ViewVC 1.0.0