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 801 - (view) (download)

1 : jhr 314 (* tga-file-io.sml
2 : jhr 274 *
3 :     * COPYRIGHT (c) 2008 John Reppy (http://cs.uchicago.edu/~jhr)
4 :     * All rights reserved.
5 : jhr 321 *
6 :     * TODO: writeFile & writeImage
7 :     * Use ImageIOUtil.raiseIO for errors
8 : jhr 553 * Implement loadFile, loadImage, and output functions
9 : jhr 274 *)
10 :    
11 : jhr 553 structure TGAFileIO : IMAGE_FILE_IO =
12 :     struct
13 : jhr 273
14 : jhr 553 structure IIO = ImageIO
15 : jhr 273 structure W8V = Word8Vector
16 :    
17 : jhr 553 val name = "Truevision TGA image format"
18 :     val suffix = "tga"
19 : jhr 274
20 : jhr 553 (* the image formats that can be stored using this file format *)
21 :     val supported = [IIO.GRAY8_FMT, IIO.GRAY_ALPHA8_FMT, IIO.BGR8_FMT, IIO.BGRA8_FMT]
22 : jhr 315
23 : jhr 273 (* TGA file-header layout *)
24 :     val hdrSz = 18
25 :     val offIDLength = 0
26 :     val offCMapType = 1
27 :     val offImageType = 2
28 :     val offCMapSpec = 3
29 :     val offWid = 12
30 :     val offHt = 14
31 :     val offBPP = 16
32 :     val offImgDesc = 17
33 :    
34 : jhr 408 fun getByte (vec, i) = Word.fromLargeWord(Word8.toLargeWord(W8V.sub(vec, i)))
35 : jhr 274 fun getShort (vec, i) = Word.toIntX(Word.orb(
36 : jhr 273 Word.<< (getByte(vec, i+1), 0w8),
37 :     getByte(vec, i)))
38 :    
39 : jhr 553 fun error msg = raise Fail msg
40 :    
41 : jhr 273 (* read the file header *)
42 :     fun readHdr inS = let
43 :     val vec = BinIO.inputN (inS, hdrSz)
44 :     in
45 :     if (W8V.length vec <> hdrSz)
46 : jhr 274 then error "bogus TGA file"
47 : jhr 273 else let
48 :     val idLen = getByte(vec, offIDLength)
49 :     val cmapTy = getByte(vec, offCMapType)
50 : jhr 274 val (color, rle) = (case getByte(vec, offImageType)
51 :     of 0w2 => (true, false)
52 :     | 0w3 => (false, false)
53 :     | 0w10 => (true, true)
54 :     | 0w11 => (false, true)
55 :     | n => error "unsupported image type"
56 : jhr 273 (* end case *))
57 :     val wid = getShort(vec, offWid)
58 :     val ht = getShort(vec, offHt)
59 : jhr 274 val bytesPP = Word.toIntX(getByte(vec, offBPP) div 0w8)
60 : jhr 273 val imageDesc = getByte(vec, offImgDesc)
61 : jhr 408 val alpha = Word.andb(imageDesc, 0wxF)
62 : jhr 331 val topFirst = (Word.andb(imageDesc, 0wx20) <> 0w0)
63 : jhr 553 val fmt = (case (color, alpha, bytesPP)
64 :     of (true, 0w0, 3) => IIO.BGR8_FMT
65 :     | (false, 0w0, 1) => IIO.GRAY8_FMT
66 :     | (false, 0w8, 2) => IIO.GRAY_ALPHA8_FMT
67 :     | (true, 0w8, 4) => IIO.BGRA8_FMT
68 :     | _ => error "unsupported format"
69 :     (* end case *))
70 : jhr 273 in
71 :     (* do some sanity checking *)
72 : jhr 274 if (cmapTy <> 0w0) then error "ill-formed TGA header" else ();
73 : jhr 331 if (Word.andb(imageDesc, 0wx10) <> 0w0) then error "right-to-left image" else ();
74 : jhr 273 (* skip the ID field, if present *)
75 :     if (idLen <> 0w0)
76 : jhr 274 then ignore(BinIO.inputN(inS, Word.toIntX idLen))
77 : jhr 273 else ();
78 :     (* return the header information *)
79 :     { wid = wid,
80 :     ht = ht,
81 : jhr 553 fmt = fmt,
82 : jhr 321 size = wid * ht,
83 : jhr 331 compressed = rle,
84 :     topFirst = topFirst
85 : jhr 273 }
86 :     end
87 :     end
88 :    
89 : jhr 553 fun getInfo fileName = ImageIOUtil.withBinIn (fileName, fn inS => let
90 :     val hdr = readHdr inS
91 :     in {
92 :     wid = #wid hdr, ht = #ht hdr, topFirst = #topFirst hdr, fmt = #fmt hdr
93 :     } end)
94 :    
95 : jhr 326 (* input routines for uncompressed files *)
96 : jhr 274 local
97 :     fun getGrayPixel (v, i) = W8V.sub(v, i)
98 :     fun getGrayAlphaPixel (v, i) = (W8V.sub(v, i), W8V.sub(v, i+1))
99 : jhr 331 fun getBGRPixel (v, i) = (W8V.sub(v, i), W8V.sub(v, i+1), W8V.sub(v, i+2))
100 :     fun getBGRAPixel (v, i) = (W8V.sub(v, i), W8V.sub(v, i+1), W8V.sub(v, i+2), W8V.sub(v, i+3))
101 : jhr 274 fun readRow (bpp, get, put) (inS, buf, i, wid) = let
102 :     val n = bpp*wid
103 :     val v = BinIO.inputN (inS, n)
104 :     fun lp j = if (j < wid)
105 : jhr 408 then (put(buf, i+j, get(v, bpp*j)); lp (j+1))
106 : jhr 274 else i+wid
107 :     in
108 :     if (W8V.length v <> n)
109 :     then error "error reading file"
110 :     else lp 0
111 :     end
112 :     in
113 :     val readGrayRow = readRow (1, getGrayPixel, DataBuffer.setub)
114 :     val readGrayAlphaRow = readRow (2, getGrayAlphaPixel, DataBuffer.set2ub)
115 : jhr 331 val readBGRRow = readRow (3, getBGRPixel, DataBuffer.set3ub)
116 : jhr 408 val readBGRARow = readRow (4, getBGRAPixel, DataBuffer.set4ub)
117 : jhr 274 end
118 : jhr 273
119 : jhr 326 (* input routines for compressed files.
120 :     * TGA files use RLE encoding, where each run starts with a one-byte header. The
121 :     * high bit of the header specifies is 1 if the run is a single pixel value and 0
122 :     * otherwise. The other 7 bits secify the length of the run-1.
123 :     *)
124 : jhr 274 local
125 : jhr 326 (* parse a packet header in a RLE image *)
126 :     fun parsePacketHdr ub = let
127 :     val np = Word8.andb(ub, 0wx7f)
128 :     in
129 :     {raw = (ub = np), nPixels = Word8.toInt np + 1}
130 :     end
131 : jhr 274 fun getGrayPixel inS = valOf(BinIO.input1 inS)
132 :     fun getGrayAlphaPixel inS = let
133 :     val v = BinIO.inputN(inS, 2)
134 :     in
135 :     (W8V.sub(v, 0), W8V.sub(v, 1))
136 :     end
137 : jhr 331 fun getBGRPixel inS = let
138 : jhr 274 val v = BinIO.inputN(inS, 3)
139 :     in
140 :     (W8V.sub(v, 0), W8V.sub(v, 1), W8V.sub(v, 2))
141 :     end
142 : jhr 331 fun getBGRAPixel inS = let
143 : jhr 274 val v = BinIO.inputN(inS, 4)
144 :     in
145 :     (W8V.sub(v, 0), W8V.sub(v, 1), W8V.sub(v, 2), W8V.sub(v, 3))
146 :     end
147 :    
148 :     (* read and uncompress a row of an REL encoded image *)
149 :     fun readRLERow (get, put) (inS, buf, i, wid) = let
150 :     val stop = i + wid
151 :     fun lp i = if (i < stop)
152 :     then let
153 :     val {raw, nPixels} = parsePacketHdr(valOf(BinIO.input1 inS))
154 :     in
155 :     if raw
156 :     then let
157 :     fun read (i, 0) = lp i
158 :     | read (i, n) = (put(buf, i, get inS); read(i+1, n-1))
159 :     in
160 :     read (i, nPixels)
161 :     end
162 :     else let
163 :     val pix = get inS
164 :     fun repeat (i, 0) = lp i
165 :     | repeat (i, n) = (put(buf, i, pix); repeat(i+1, n-1))
166 :     in
167 :     repeat (i, nPixels)
168 :     end
169 :     end
170 :     else i
171 :     in
172 :     lp i
173 :     end
174 :     in
175 : jhr 273 val readGrayRLERow = readRLERow (getGrayPixel, DataBuffer.setub)
176 :     val readGrayAlphaRLERow = readRLERow (getGrayAlphaPixel, DataBuffer.set2ub)
177 : jhr 331 val readBGRRLERow = readRLERow (getBGRPixel, DataBuffer.set3ub)
178 :     val readBGRARLERow = readRLERow (getBGRAPixel, DataBuffer.set4ub)
179 : jhr 274 end
180 : jhr 273
181 : jhr 318 (* read an image row by row *)
182 :     fun readRaster readRow (inS, wid, ht, buf) = let
183 : jhr 273 val stop = wid * ht
184 :     fun lp i = if (i < stop)
185 :     then lp (readRow (inS, buf, i, wid))
186 :     else ()
187 :     in
188 :     lp 0
189 :     end
190 :    
191 :     (* read an image while flipping the Y axis *)
192 : jhr 318 fun flipRaster readRow (inS, wid, ht, buf) = let
193 : jhr 273 fun lp i = if (i < 0)
194 :     then ()
195 :     else (ignore (readRow (inS, buf, i, wid)); lp(i-wid))
196 :     in
197 :     lp ((ht - 1) * wid)
198 :     end
199 :    
200 : jhr 553 fun readFile' {file, flip} = ImageIOUtil.withBinIn (file, fn inS => let
201 :     val {wid, ht, size, fmt, compressed, topFirst} = readHdr inS
202 :     val flip = (topFirst <> flip)
203 : jhr 273 in
204 : jhr 553 case fmt
205 :     of IIO.GRAY8_FMT => let
206 : jhr 801 val buf = DataBuffer.new (DataBuffer.tyub, size)
207 : jhr 553 in
208 :     case (flip, compressed)
209 :     of (false, false) => readRaster readGrayRow (inS, wid, ht, buf)
210 :     | (false, true) => readRaster readGrayRLERow (inS, wid, ht, buf)
211 :     | (true, false) => flipRaster readGrayRow (inS, wid, ht, buf)
212 :     | (true, true) => flipRaster readGrayRLERow (inS, wid, ht, buf)
213 :     (* end case *);
214 :     {wid = wid, ht = ht, buf = IIO.GRAY8_BUF buf}
215 :     end
216 :     | IIO.GRAY_ALPHA8_FMT => let
217 : jhr 801 val buf = DataBuffer.new (DataBuffer.ty2ub, size)
218 : jhr 553 in
219 :     case (flip, compressed)
220 :     of (false, false) => readRaster readGrayAlphaRow (inS, wid, ht, buf)
221 :     | (false, true) => readRaster readGrayAlphaRLERow (inS, wid, ht, buf)
222 :     | (true, false) => flipRaster readGrayAlphaRow (inS, wid, ht, buf)
223 :     | (true, true) => flipRaster readGrayAlphaRLERow (inS, wid, ht, buf)
224 :     (* end case *);
225 :     {wid = wid, ht = ht, buf = IIO.GRAY_ALPHA8_BUF buf}
226 :     end
227 :     | IIO.BGR8_FMT => let
228 : jhr 801 val buf = DataBuffer.new (DataBuffer.ty3ub, size)
229 : jhr 553 in
230 :     case (flip, compressed)
231 :     of (false, false) => readRaster readBGRRow (inS, wid, ht, buf)
232 :     | (false, true) => readRaster readBGRRLERow (inS, wid, ht, buf)
233 :     | (true, false) => flipRaster readBGRRow (inS, wid, ht, buf)
234 :     | (true, true) => flipRaster readBGRRLERow (inS, wid, ht, buf)
235 :     (* end case *);
236 :     {wid = wid, ht = ht, buf = IIO.BGR8_BUF buf}
237 :     end
238 :     | IIO.BGRA8_FMT => let
239 : jhr 801 val buf = DataBuffer.new (DataBuffer.ty4ub, size)
240 : jhr 553 in
241 :     case (flip, compressed)
242 :     of (false, false) => readRaster readBGRARow (inS, wid, ht, buf)
243 :     | (false, true) => readRaster readBGRARLERow (inS, wid, ht, buf)
244 :     | (true, false) => flipRaster readBGRARow (inS, wid, ht, buf)
245 :     | (true, true) => flipRaster readBGRARLERow (inS, wid, ht, buf)
246 :     (* end case *);
247 :     {wid = wid, ht = ht, buf = IIO.BGRA8_BUF buf}
248 :     end
249 :     | _ => raise Fail "impossible"
250 :     (* end case *)
251 : jhr 318 end)
252 : jhr 553 fun readFile file = readFile' {file=file, flip=false}
253 : jhr 273
254 : jhr 553 fun readImage' arg = ImageIOUtil.dataToImage (readFile' arg)
255 :     fun readImage file = readImage' {file=file, flip=false}
256 : jhr 315
257 : jhr 553 (* load an image into an existing buffer; the size and format must match *)
258 :     fun loadFile {file, flip, data} = raise Fail "TGAFileIO.loadFile unimplemented"
259 :    
260 :     (* load an image into an existing image; the size and format must match *)
261 :     fun loadImage {file, flip, img} = raise Fail "TGAFileIO.loadImage unimplemented"
262 :    
263 :     (* write an image buffer *)
264 :     fun writeFile (file, data) = raise Fail "TGAFileIO.writeFile unimplemented"
265 :     fun writeFile' {file, flip, data} = raise Fail "TGAFileIO.writeFile' unimplemented"
266 :    
267 :     (* write an image *)
268 :     fun writeImage (file, img) = raise Fail "TGAFileIO.writeImage unimplemented"
269 :     fun writeImage' {file, flip, img} = raise Fail "TGAFileIO.writeImage' unimplemented"
270 :    
271 : jhr 273 end
272 :    

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