34 |
34 |
35 !TIFFReader class methodsFor:'documentation'! |
35 !TIFFReader class methodsFor:'documentation'! |
36 |
36 |
37 documentation |
37 documentation |
38 " |
38 " |
39 This class knows how to read TIFF files (and will |
39 This class knows how to read TIFF files and how to |
40 learn sometime in the future how to write them). |
40 write uncompressed TIFF files. |
41 Currently, not all formats are implemented and of |
41 Only single image files are supported. |
42 those that are, not all are tested. |
42 Currently, not all formats are implemented, and of |
|
43 those that are not all are tested. |
43 It should work with most rgb, mono and 2-plane greyscale |
44 It should work with most rgb, mono and 2-plane greyscale |
44 images, since this is what I have as test material on |
45 images, since this is what I have as test material on |
45 the NeXT. |
46 the NeXT. |
46 It supports uncompressed, LZW and G3 compressed images; |
47 It supports reading of uncompressed, LZW and G3 compressed |
47 JPEG is currently not implemented. |
48 images; JPEG and pacbits are currently not implemented. |
48 More formats and compressions will come ... |
49 Only writing of uncompressed images is currently implemented. |
|
50 More formats will come ... |
49 " |
51 " |
50 ! ! |
52 ! ! |
51 |
53 |
|
54 !TIFFReader class methodsFor:'testing'! |
|
55 |
|
56 isValidImageFile:aFileName |
|
57 "return true, if aFileName contains a GIF image" |
|
58 |
|
59 |inStream char1 char2 version| |
|
60 |
|
61 inStream := FileStream readonlyFileNamed:aFileName. |
|
62 inStream isNil ifTrue:[^ false]. |
|
63 |
|
64 char1 := inStream next. |
|
65 char2 := inStream next. |
|
66 |
|
67 ((char1 ~~ char2) or:[(char1 ~~ $I) and:[char1 ~~ $M]]) ifTrue:[ |
|
68 inStream close. |
|
69 ^ false |
|
70 ]. |
|
71 |
|
72 inStream binary. |
|
73 version := inStream nextShortMSB:(char1 == $M). |
|
74 inStream close. |
|
75 |
|
76 (version ~~ 42) ifTrue:[^ false]. |
|
77 ^ true |
|
78 ! ! |
|
79 |
52 !TIFFReader methodsFor:'reading from file'! |
80 !TIFFReader methodsFor:'reading from file'! |
53 |
81 |
54 fromFile:aFileName |
82 fromFile:aFileName |
|
83 "read an image from aFileName" |
|
84 |
55 |char1 char2 version |
85 |char1 char2 version |
56 numberOfTags "{ Class: SmallInteger }" |
86 numberOfTags "{ Class: SmallInteger }" |
57 tagType "{ Class: SmallInteger }" |
87 tagType "{ Class: SmallInteger }" |
58 numberType "{ Class: SmallInteger }" |
88 numberType "{ Class: SmallInteger }" |
59 length "{ Class: SmallInteger }" |
89 length "{ Class: SmallInteger }" |
108 |
140 |
109 offset := self readLong + 1. |
141 offset := self readLong + 1. |
110 inStream position:offset. |
142 inStream position:offset. |
111 |
143 |
112 numberOfTags := self readShort. |
144 numberOfTags := self readShort. |
113 1 to:(numberOfTags) do:[:index | |
145 1 to:numberOfTags do:[:index | |
114 tagType := self readShort. |
146 tagType := self readShort. |
115 numberType := self readShort. |
147 numberType := self readShort. |
116 length := self readLong. |
148 length := self readLong. |
117 self decodeTiffTag:tagType numberType:numberType |
149 self decodeTiffTag:tagType numberType:numberType length:length |
118 length:length |
|
119 ]. |
150 ]. |
120 |
151 |
121 offset := self readLong. |
152 offset := self readLong. |
122 (offset ~~ 0) ifTrue:[ |
153 (offset ~~ 0) ifTrue:[ |
123 'more tags ignored' printNewline |
154 'more tags ignored' printNewline |
124 ]. |
155 ]. |
125 |
156 |
|
157 "check for required tags" |
126 ok := true. |
158 ok := true. |
127 width isNil ifTrue:[ |
159 width isNil ifTrue:[ |
128 'missing width tag' printNewline. |
160 'missing width tag' printNewline. |
129 ok := false |
161 ok := false |
130 ]. |
162 ]. |
192 ! ! |
224 ! ! |
193 |
225 |
194 !TIFFReader methodsFor:'writing to file'! |
226 !TIFFReader methodsFor:'writing to file'! |
195 |
227 |
196 save:image onFile:aFileName |
228 save:image onFile:aFileName |
197 "save image as TIFF file on aFileName" |
229 "save image as (uncompressed) TIFF file on aFileName" |
198 |
230 |
199 |pos1 pos| |
231 |pos1 pos| |
200 |
232 |
201 outStream := FileStream newFileNamed:aFileName. |
233 outStream := FileStream newFileNamed:aFileName. |
202 outStream isNil ifTrue:[ |
234 outStream isNil ifTrue:[ |
203 'create error' printNewline. |
235 'create error' printNewline. |
204 ^ nil |
236 ^ nil |
205 ]. |
237 ]. |
206 |
238 |
|
239 "save as msb" |
|
240 |
207 byteOrder := #msb. |
241 byteOrder := #msb. |
|
242 " |
|
243 byteOrder := #lsb. |
|
244 " |
208 fillOrder := #msb. |
245 fillOrder := #msb. |
209 width := image width. |
246 width := image width. |
210 height := image height. |
247 height := image height. |
211 photometric := image photometric. |
248 photometric := image photometric. |
212 samplesPerPixel := image samplesPerPixel. |
249 samplesPerPixel := image samplesPerPixel. |
214 colorMap := image colorMap. |
251 colorMap := image colorMap. |
215 planarConfiguration := 1. |
252 planarConfiguration := 1. |
216 compression := 1. "none" |
253 compression := 1. "none" |
217 data := image bits. |
254 data := image bits. |
218 |
255 |
219 "save as msb" |
|
220 |
256 |
221 currentOffset := 0. |
257 currentOffset := 0. |
222 |
258 |
223 outStream nextPut:$M. |
259 (byteOrder == #msb) ifTrue:[ |
224 outStream nextPut:$M. |
260 outStream nextPut:$M. |
|
261 outStream nextPut:$M. |
|
262 ] ifFalse:[ |
|
263 outStream nextPut:$I. |
|
264 outStream nextPut:$I. |
|
265 ]. |
225 currentOffset := currentOffset + 2. |
266 currentOffset := currentOffset + 2. |
226 |
267 |
227 outStream binary. |
268 outStream binary. |
228 |
269 |
229 self writeShort:42. "version" |
270 self writeShort:42. |
230 currentOffset := currentOffset + 2. |
271 currentOffset := currentOffset + 2. |
231 |
272 |
232 pos1 := outStream position. |
273 pos1 := outStream position. |
233 self writeLong:0. "start of commands - filled in later" |
274 self writeLong:0. "start of tags - filled in later" |
234 currentOffset := currentOffset + 4. |
275 currentOffset := currentOffset + 4. |
235 |
276 |
236 "output strips" |
277 "output strips" |
237 |
278 |
238 self writeBits. "this outputs bits as strips, sets stripOffsets and stripByteCounts" |
279 self writeBits. "this outputs bits as strips, sets stripOffsets and stripByteCounts" |
241 self writeBitsPerSample. "this outputs bitsPerSample, sets bitsPerSamplePos" |
282 self writeBitsPerSample. "this outputs bitsPerSample, sets bitsPerSamplePos" |
242 photometric == #palette ifTrue:[ |
283 photometric == #palette ifTrue:[ |
243 self writeColorMap "this outputs colorMap, sets colorMapPos" |
284 self writeColorMap "this outputs colorMap, sets colorMapPos" |
244 ]. |
285 ]. |
245 |
286 |
246 pos := outStream position. "backpatch tag offset" |
287 pos := outStream position. "backpatch tag offset" |
247 outStream position:pos1. |
288 outStream position:pos1. |
248 self writeLong:(pos - 1). |
289 self writeLong:(pos - 1). "fill in tag offset" |
249 outStream position:pos. |
290 outStream position:pos. |
|
291 " |
250 ('patch tag offset at: ', (pos1 printStringRadix:16) , ' to ', |
292 ('patch tag offset at: ', (pos1 printStringRadix:16) , ' to ', |
251 (pos printStringRadix:16)) printNewline. |
293 (pos printStringRadix:16)) printNewline. |
252 |
294 " |
253 "output tag data" |
295 "output tag data" |
254 |
296 |
255 photometric == #palette ifTrue:[ |
297 photometric == #palette ifTrue:[ |
256 self writeShort:9 |
298 self writeShort:9. "9 tags" |
257 ] ifFalse:[ |
299 ] ifFalse:[ |
258 self writeShort:8. "8 tags" |
300 self writeShort:8. "8 tags" |
259 ]. |
301 ]. |
260 self writeTag:256. "image width" |
302 self writeTag:256. "image width" |
261 self writeTag:257. "image height" |
303 self writeTag:257. "image height" |
262 self writeTag:258. "bits per sample" |
304 self writeTag:258. "bits per sample" |
263 self writeTag:259. "compression" |
305 self writeTag:259. "compression" |
266 self writeTag:279. "strip byte counts" |
308 self writeTag:279. "strip byte counts" |
267 self writeTag:284. "planarconfig" |
309 self writeTag:284. "planarconfig" |
268 photometric == #palette ifTrue:[ |
310 photometric == #palette ifTrue:[ |
269 self writeTag:320 "colorMap" |
311 self writeTag:320 "colorMap" |
270 ]. |
312 ]. |
271 self writeLong:0. |
313 self writeLong:0. "end of tags mark" |
272 |
|
273 outStream close |
|
274 ! ! |
314 ! ! |
275 |
315 |
276 !TIFFReader methodsFor:'private'! |
316 !TIFFReader methodsFor:'private'! |
277 |
317 |
278 readLongs:n |
318 readLongs:n |
279 |oldPos offset values| |
319 |oldPos offset values| |
280 |
320 |
281 values := Array new:n. |
321 values := Array new:n. |
282 (n == 1) ifTrue:[ |
322 (n == 1) ifTrue:[ |
283 values at:1 put:(self readLong) |
323 values at:1 put:self readLong. |
284 ] ifFalse:[ |
324 ] ifFalse:[ |
285 offset := self readLong. |
325 offset := self readLong. |
286 oldPos := inStream position. |
326 oldPos := inStream position. |
287 inStream position:(offset + 1). |
327 inStream position:(offset + 1). |
288 1 to:n do:[:index | |
328 1 to:n do:[:index | |
289 values at:index put:(self readLong) |
329 values at:index put:self readLong |
290 ]. |
330 ]. |
291 inStream position:oldPos |
331 inStream position:oldPos |
292 ]. |
332 ]. |
293 ^ values |
333 ^ values |
294 ! |
334 ! |
302 readShorts:n |
342 readShorts:n |
303 |oldPos offset values| |
343 |oldPos offset values| |
304 |
344 |
305 values := Array new:n. |
345 values := Array new:n. |
306 (n <= 2) ifTrue:[ |
346 (n <= 2) ifTrue:[ |
307 values at:1 put:(self readShort). |
347 values at:1 put:self readShort. |
308 (n == 2) ifTrue:[ |
348 (n == 2) ifTrue:[ |
309 values at:2 put:(self readShort) |
349 values at:2 put:self readShort |
310 ] ifFalse:[ |
350 ] ifFalse:[ |
311 self readShort |
351 self readShort |
312 ] |
352 ] |
313 ] ifFalse:[ |
353 ] ifFalse:[ |
314 offset := self readLong. |
354 offset := self readLong. |
315 oldPos := inStream position. |
355 oldPos := inStream position. |
316 inStream position:(offset + 1). |
356 inStream position:(offset + 1). |
317 1 to:n do:[:index | |
357 1 to:n do:[:index | |
318 values at:index put:(self readShort) |
358 values at:index put:self readShort |
319 ]. |
359 ]. |
320 inStream position:oldPos |
360 inStream position:oldPos |
321 ]. |
361 ]. |
322 ^ values |
362 ^ values |
323 ! |
363 ! |
704 stripByteCounts := (Array new:height) atAllPut:bytesPerRow. |
744 stripByteCounts := (Array new:height) atAllPut:bytesPerRow. |
705 |
745 |
706 offs := 1. |
746 offs := 1. |
707 1 to:height do:[:row | |
747 1 to:height do:[:row | |
708 stripOffsets at:row put:(outStream position - 1). |
748 stripOffsets at:row put:(outStream position - 1). |
709 outStream nextPutBytes:data size from:data startingAt:offs. |
749 outStream nextPutBytes:bytesPerRow from:data startingAt:offs. |
710 offs := offs + bytesPerRow |
750 offs := offs + bytesPerRow |
711 ]. |
751 ]. |
712 rowsPerStrip := 1 |
752 rowsPerStrip := 1 |
713 ]. |
753 ]. |
714 'stripOffsets: ' print. stripOffsets printNewline. |
754 'stripOffsets: ' print. stripOffsets printNewline. |
718 writeColorMap |
758 writeColorMap |
719 colorMapPos := outStream position. |
759 colorMapPos := outStream position. |
720 colorMap do:[:subMap | |
760 colorMap do:[:subMap | |
721 subMap do:[:entry | |
761 subMap do:[:entry | |
722 "my maps are 8 bit - tiff map is 16 bit" |
762 "my maps are 8 bit - tiff map is 16 bit" |
723 |
763 entry isNil ifTrue:[ |
724 self writeShort:(entry / 255 * 16rFFFF) rounded |
764 "unused map entry" |
|
765 self writeShort:0 |
|
766 ] ifFalse:[ |
|
767 self writeShort:(entry / 255 * 16rFFFF) rounded |
|
768 ] |
725 ] |
769 ] |
726 ] |
770 ] |
727 ! |
771 ! |
728 |
772 |
729 writeStripOffsets |
773 writeStripOffsets |
|
774 " |
730 'stripOffsets: ' print. stripOffsets printNewline. |
775 'stripOffsets: ' print. stripOffsets printNewline. |
731 'store stripoffsets at: ' print. outStream position printNewline. |
776 'store stripoffsets at: ' print. outStream position printNewline. |
|
777 " |
732 stripOffsetsPos := outStream position. |
778 stripOffsetsPos := outStream position. |
733 stripOffsets do:[:o | |
779 stripOffsets do:[:o | |
734 self writeLong:o |
780 self writeLong:o |
735 ] |
781 ] |
736 ! |
782 ! |
737 |
783 |
738 writeStripByteCounts |
784 writeStripByteCounts |
|
785 " |
739 'stripByteCounts: ' print. stripByteCounts printNewline. |
786 'stripByteCounts: ' print. stripByteCounts printNewline. |
740 'store stripbytecounts at: ' print. outStream position printNewline. |
787 'store stripbytecounts at: ' print. outStream position printNewline. |
|
788 " |
741 stripByteCountsPos := outStream position. |
789 stripByteCountsPos := outStream position. |
742 stripByteCounts do:[:c | |
790 stripByteCounts do:[:c | |
743 self writeShort:c |
791 self writeShort:c |
744 ] |
792 ] |
745 ! |
793 ! |
746 |
794 |
747 writeBitsPerSample |
795 writeBitsPerSample |
|
796 " |
748 'bitsPerSample: ' print. bitsPerSample printNewline. |
797 'bitsPerSample: ' print. bitsPerSample printNewline. |
749 'store bitspersample at: ' print. outStream position printNewline. |
798 'store bitspersample at: ' print. outStream position printNewline. |
|
799 " |
750 bitsPerSamplePos := outStream position. |
800 bitsPerSamplePos := outStream position. |
751 bitsPerSample do:[:n | |
801 bitsPerSample do:[:n | |
752 self writeShort:n |
802 self writeShort:n |
753 ] |
803 ] |
754 ! |
804 ! |
1173 offset := offset + bytesPerStrip. |
1223 offset := offset + bytesPerStrip. |
1174 row := row + rowsPerStrip |
1224 row := row + rowsPerStrip |
1175 ]. |
1225 ]. |
1176 |
1226 |
1177 (predictor == 2) ifTrue:[ |
1227 (predictor == 2) ifTrue:[ |
1178 self class decodeDelta:3 in:data width:width height:height |
1228 self class decodeDelta:3 in:data width:width height:height |
1179 ] |
1229 ] |
1180 ! |
1230 ! |
1181 |
1231 |
1182 readCCITTGroup3TiffImageData |
1232 readCCITTGroup3TiffImageData |
1183 "not really tested - all I got is a single |
1233 "not really tested - all I got is a single |
1214 bytesPerRow := bitsPerRow // 8. |
1264 bytesPerRow := bitsPerRow // 8. |
1215 ((bitsPerRow \\ 8) ~~ 0) ifTrue:[ |
1265 ((bitsPerRow \\ 8) ~~ 0) ifTrue:[ |
1216 bytesPerRow := bytesPerRow + 1 |
1266 bytesPerRow := bytesPerRow + 1 |
1217 ]. |
1267 ]. |
1218 |
1268 |
1219 data := ByteArray uninitializedNew:(bytesPerRow * height). |
1269 data := ByteArray new:(bytesPerRow * height). |
1220 compressedStrip := ByteArray uninitializedNew:bytesPerRow. |
1270 compressedStrip := ByteArray uninitializedNew:bytesPerRow. |
1221 |
1271 |
1222 offset := 1. |
1272 offset := 1. |
1223 stripNr := 0. |
1273 stripNr := 0. |
1224 |
1274 |
1225 row := 1. |
1275 row := 1. |
1226 bytesPerStrip := bytesPerRow * rowsPerStrip. |
1276 bytesPerStrip := bytesPerRow * rowsPerStrip. |
1227 [row <= height] whileTrue:[ |
1277 [row <= height] whileTrue:[ |
1228 stripNr := stripNr + 1. |
1278 stripNr := stripNr + 1. |
1229 inStream position:((stripOffsets at:stripNr) + 1). |
1279 inStream position:((stripOffsets at:stripNr) + 1). |
1230 inStream nextBytes:(stripByteCounts at:stripNr) |
1280 inStream nextBytes:(stripByteCounts at:stripNr) into:compressedStrip. |
1231 into:compressedStrip. |
|
1232 self class decompressCCITT3From:compressedStrip |
1281 self class decompressCCITT3From:compressedStrip |
1233 into:data |
1282 into:data |
1234 startingAt:offset |
1283 startingAt:offset |
1235 count:width. |
1284 count:width. |
1236 offset := offset + bytesPerStrip. |
1285 offset := offset + bytesPerStrip. |