0
|
1 |
"
|
6
|
2 |
COPYRIGHT (c) 1991 by Claus Gittinger
|
26
|
3 |
All Rights Reserved
|
0
|
4 |
|
|
5 |
This software is furnished under a license and may be used
|
|
6 |
only in accordance with the terms of that license and with the
|
|
7 |
inclusion of the above copyright notice. This software may not
|
|
8 |
be provided or otherwise made available to, or used by, any
|
|
9 |
other person. No title to or ownership of the software is
|
|
10 |
hereby transferred.
|
|
11 |
"
|
|
12 |
|
|
13 |
ImageReader subclass:#GIFReader
|
154
|
14 |
instanceVariableNames:'redMap greenMap blueMap'
|
|
15 |
classVariableNames:''
|
|
16 |
poolDictionaries:''
|
|
17 |
category:'Graphics-Images support'
|
0
|
18 |
!
|
|
19 |
|
|
20 |
!GIFReader class methodsFor:'documentation'!
|
|
21 |
|
21
|
22 |
copyright
|
|
23 |
"
|
|
24 |
COPYRIGHT (c) 1991 by Claus Gittinger
|
26
|
25 |
All Rights Reserved
|
21
|
26 |
|
|
27 |
This software is furnished under a license and may be used
|
|
28 |
only in accordance with the terms of that license and with the
|
|
29 |
inclusion of the above copyright notice. This software may not
|
|
30 |
be provided or otherwise made available to, or used by, any
|
|
31 |
other person. No title to or ownership of the software is
|
|
32 |
hereby transferred.
|
|
33 |
"
|
|
34 |
!
|
|
35 |
|
0
|
36 |
documentation
|
|
37 |
"
|
|
38 |
this class provides methods for loading and saving GIF pictures.
|
21
|
39 |
It has been tested with some different GIF87a pictures, I dont
|
0
|
40 |
know, if it works with other GIF versions.
|
|
41 |
GIF extension blocks are not handled.
|
|
42 |
|
|
43 |
GIF file writing is not implemented (use TIFF).
|
|
44 |
|
21
|
45 |
legal stuff extracted from GIF87a documentation:
|
0
|
46 |
|
|
47 |
CompuServe Incorporated hereby grants a limited, non-exclusive, royalty-free
|
|
48 |
license for the use of the Graphics Interchange Format(sm) in computer
|
|
49 |
software; computer software utilizing GIF(sm) must acknowledge ownership of the
|
|
50 |
Graphics Interchange Format and its Service Mark by CompuServe Incorporated, in
|
|
51 |
User and Technical Documentation.
|
|
52 |
|
|
53 |
The Graphics Interchange Format(c) is the Copyright property of
|
|
54 |
CompuServe Incorporated. GIF(sm) is a Service Mark property of
|
|
55 |
CompuServe Incorporated.
|
194
|
56 |
|
|
57 |
[See also:]
|
|
58 |
BlitImageReader FaceReader JPEGReader PBMReader PCXReader
|
210
|
59 |
ST80FormReader SunRasterReader TargaReader TIFFReader WindowsIconReader
|
209
|
60 |
XBMReader XPMReader XWDReader
|
220
|
61 |
|
|
62 |
[author:]
|
|
63 |
Claus Gittinger
|
0
|
64 |
"
|
|
65 |
! !
|
|
66 |
|
26
|
67 |
!GIFReader class methodsFor:'initialization'!
|
|
68 |
|
|
69 |
initialize
|
198
|
70 |
"install myself in the Image classes fileFormat table
|
|
71 |
for the `.gif', and '.GIF' extensions."
|
|
72 |
|
26
|
73 |
Image fileFormats at:'.gif' put:self.
|
|
74 |
Image fileFormats at:'.GIF' put:self.
|
198
|
75 |
|
|
76 |
"Modified: 23.4.1996 / 12:28:16 / cg"
|
26
|
77 |
! !
|
|
78 |
|
1
|
79 |
!GIFReader class methodsFor:'testing'!
|
|
80 |
|
|
81 |
isValidImageFile:aFileName
|
|
82 |
"return true, if aFileName contains a GIF image"
|
|
83 |
|
|
84 |
|id inStream|
|
|
85 |
|
9
|
86 |
inStream := self streamReadingFile:aFileName.
|
1
|
87 |
inStream isNil ifTrue:[^ false].
|
|
88 |
|
|
89 |
id := String new:6.
|
|
90 |
inStream nextBytes:6 into:id.
|
|
91 |
inStream close.
|
|
92 |
|
|
93 |
(id = 'GIF87a') ifFalse:[
|
174
|
94 |
(id startsWith:'GIF') ifFalse:[^ false].
|
1
|
95 |
|
174
|
96 |
id ~= 'GIF89a' ifTrue:[
|
|
97 |
'GIFReader: not a GIF87a/GIF89a file - hope that works' infoPrintNL.
|
|
98 |
]
|
1
|
99 |
].
|
|
100 |
^ true
|
174
|
101 |
|
|
102 |
"Modified: 7.3.1996 / 19:16:52 / cg"
|
1
|
103 |
! !
|
|
104 |
|
0
|
105 |
!GIFReader methodsFor:'reading from file'!
|
|
106 |
|
1
|
107 |
checkGreyscaleColormap
|
|
108 |
"return true, if colormap is really a greymap"
|
|
109 |
|
20
|
110 |
|sz "{ Class: SmallInteger }"
|
|
111 |
redVal|
|
|
112 |
|
|
113 |
sz := redMap size.
|
|
114 |
|
|
115 |
1 to:sz do:[:i |
|
26
|
116 |
redVal := redMap at:i.
|
|
117 |
redVal ~~ (greenMap at:i) ifTrue:[^ false].
|
|
118 |
redVal ~~ (blueMap at:i) ifTrue:[^ false].
|
1
|
119 |
].
|
|
120 |
^ true
|
|
121 |
!
|
|
122 |
|
43
|
123 |
fromStream:aStream
|
192
|
124 |
"read a stream containing a GIF image.
|
|
125 |
Leave image description in instance variables."
|
43
|
126 |
|
|
127 |
|byte index flag count
|
|
128 |
colorMapSize bitsPerPixel scrWidth scrHeight
|
|
129 |
hasColorMap hasLocalColorMap interlaced id
|
|
130 |
leftOffs topOffs codeLen
|
|
131 |
compressedData compressedSize
|
154
|
132 |
tmp srcOffset dstOffset isGif89
|
64
|
133 |
h "{ Class: SmallInteger }"|
|
43
|
134 |
|
|
135 |
inStream := aStream.
|
|
136 |
aStream binary.
|
|
137 |
|
|
138 |
"GIF-files are always lsb (intel-world)"
|
|
139 |
byteOrder := #lsb.
|
|
140 |
|
|
141 |
id := String new:6.
|
|
142 |
aStream nextBytes:6 into:id.
|
|
143 |
|
|
144 |
"all I had for testing where GIF87a files;
|
|
145 |
I hope later versions work too ..."
|
|
146 |
|
154
|
147 |
isGif89 := false.
|
43
|
148 |
(id ~= 'GIF87a') ifTrue:[
|
154
|
149 |
(id startsWith:'GIF') ifFalse:[
|
174
|
150 |
'GIFReader: not a gif file' infoPrintNL.
|
154
|
151 |
^ nil
|
|
152 |
].
|
|
153 |
id ~= 'GIF89a' ifTrue:[
|
174
|
154 |
'GIFReader: not a GIF87a/GIF89a file - hope that works' infoPrintNL.
|
154
|
155 |
]
|
43
|
156 |
].
|
|
157 |
|
|
158 |
"get screen dimensions (not used)"
|
|
159 |
|
|
160 |
scrWidth := aStream nextShortMSB:false.
|
|
161 |
scrHeight := aStream nextShortMSB:false.
|
|
162 |
|
|
163 |
"get flag byte"
|
|
164 |
flag := aStream nextByte.
|
|
165 |
hasColorMap := (flag bitAnd:2r10000000) ~~ 0.
|
|
166 |
"bitsPerRGB := ((flag bitAnd:2r01110000) bitShift:-4) + 1. "
|
|
167 |
"colorMapSorted := ((flag bitAnd:2r00001000) ~~ 0. "
|
|
168 |
bitsPerPixel := (flag bitAnd:2r00000111) + 1.
|
|
169 |
colorMapSize := 1 bitShift:bitsPerPixel.
|
|
170 |
|
|
171 |
"get background (not used)"
|
|
172 |
aStream nextByte.
|
|
173 |
|
|
174 |
"aspect ratio (not used)"
|
|
175 |
aStream nextByte.
|
|
176 |
|
|
177 |
"get colorMap"
|
|
178 |
hasColorMap ifTrue:[
|
154
|
179 |
self readColorMap:colorMapSize
|
43
|
180 |
].
|
|
181 |
|
154
|
182 |
"skip gif89a extensions "
|
43
|
183 |
byte := aStream nextByte.
|
154
|
184 |
[byte == 16r21] whileTrue:[
|
|
185 |
"/ extension
|
|
186 |
self readExtension:aStream.
|
|
187 |
byte := aStream nextByte.
|
|
188 |
].
|
|
189 |
|
|
190 |
"must now be image separator"
|
43
|
191 |
(byte ~~ 16r2C) ifTrue:[
|
174
|
192 |
('GIFReader: corrupted gif file (no IMAGESEP): ' , (byte printStringRadix:16)) infoPrintNL.
|
154
|
193 |
^ nil
|
43
|
194 |
].
|
|
195 |
|
|
196 |
"get image data"
|
|
197 |
leftOffs := aStream nextShortMSB:false.
|
|
198 |
topOffs := aStream nextShortMSB:false.
|
|
199 |
width := aStream nextShortMSB:false.
|
|
200 |
height := aStream nextShortMSB:false.
|
|
201 |
|
|
202 |
"
|
|
203 |
'width ' print. width printNewline.
|
|
204 |
'height ' print. height printNewline.
|
|
205 |
"
|
|
206 |
|
|
207 |
"another flag byte"
|
|
208 |
flag := aStream nextByte.
|
|
209 |
interlaced := (flag bitAnd:2r01000000) ~~ 0.
|
|
210 |
hasLocalColorMap := (flag bitAnd:2r10000000) ~~ 0.
|
|
211 |
"localColorMapSorted := (flag bitAnd:2r00100000) ~~ 0. "
|
|
212 |
|
|
213 |
"if image has a local colormap, this one is used"
|
|
214 |
|
|
215 |
hasLocalColorMap ifTrue:[
|
154
|
216 |
"local descr. overwrites"
|
|
217 |
bitsPerPixel := (flag bitAnd:2r00000111) + 1.
|
|
218 |
colorMapSize := 1 bitShift:bitsPerPixel.
|
43
|
219 |
" 'local colormap' printNewline. "
|
154
|
220 |
"overwrite colormap"
|
|
221 |
self readColorMap:colorMapSize
|
43
|
222 |
].
|
|
223 |
|
|
224 |
"get codelen for decompression"
|
|
225 |
codeLen := aStream nextByte.
|
|
226 |
|
|
227 |
compressedData := ByteArray uninitializedNew:(aStream size).
|
|
228 |
|
|
229 |
"get compressed data"
|
|
230 |
index := 1.
|
|
231 |
count := aStream nextByte.
|
|
232 |
[count notNil and:[count ~~ 0]] whileTrue:[
|
154
|
233 |
aStream nextBytes:count into:compressedData startingAt:index.
|
|
234 |
index := index + count.
|
|
235 |
count := aStream nextByte
|
43
|
236 |
].
|
|
237 |
compressedSize := index - 1.
|
|
238 |
|
64
|
239 |
h := height.
|
|
240 |
data := ByteArray new:((width + 1) * (h + 1)).
|
136
|
241 |
"/ 'GIFReader: decompressing ...' infoPrintNL.
|
43
|
242 |
|
|
243 |
self class decompressGIFFrom:compressedData
|
154
|
244 |
count:compressedSize
|
|
245 |
into:data
|
|
246 |
startingAt:1
|
|
247 |
codeLen:(codeLen + 1).
|
43
|
248 |
|
|
249 |
interlaced ifTrue:[
|
154
|
250 |
Transcript showCr:'deinterlacing'.
|
|
251 |
tmp := ByteArray new:(data size).
|
43
|
252 |
|
154
|
253 |
"phase 1: 0, 8, 16, 24, ..."
|
43
|
254 |
|
154
|
255 |
srcOffset := 1.
|
|
256 |
0 to:(h - 1) by:8 do:[:dstRow |
|
|
257 |
dstOffset := dstRow * width + 1.
|
|
258 |
tmp replaceFrom:dstOffset to:(dstOffset + width - 1)
|
|
259 |
with:data startingAt:srcOffset.
|
|
260 |
srcOffset := srcOffset + width.
|
|
261 |
].
|
43
|
262 |
|
154
|
263 |
"phase 2: 4, 12, 20, 28, ..."
|
43
|
264 |
|
154
|
265 |
4 to:(h - 1) by:8 do:[:dstRow |
|
|
266 |
dstOffset := dstRow * width + 1.
|
|
267 |
tmp replaceFrom:dstOffset to:(dstOffset + width - 1)
|
|
268 |
with:data startingAt:srcOffset.
|
|
269 |
srcOffset := srcOffset + width.
|
|
270 |
].
|
43
|
271 |
|
154
|
272 |
"phase 3: 2, 6, 10, 14, ..."
|
43
|
273 |
|
154
|
274 |
2 to:(h - 1) by:4 do:[:dstRow |
|
|
275 |
dstOffset := dstRow * width + 1.
|
|
276 |
tmp replaceFrom:dstOffset to:(dstOffset + width - 1)
|
|
277 |
with:data startingAt:srcOffset.
|
|
278 |
srcOffset := srcOffset + width.
|
|
279 |
].
|
43
|
280 |
|
154
|
281 |
"phase 4: 1, 3, 5, 7, ..."
|
43
|
282 |
|
154
|
283 |
1 to:(h - 1) by:2 do:[:dstRow |
|
|
284 |
dstOffset := dstRow * width + 1.
|
|
285 |
tmp replaceFrom:dstOffset to:(dstOffset + width - 1)
|
|
286 |
with:data startingAt:srcOffset.
|
|
287 |
srcOffset := srcOffset + width.
|
|
288 |
].
|
43
|
289 |
|
154
|
290 |
data := tmp.
|
|
291 |
tmp := nil
|
43
|
292 |
].
|
|
293 |
|
|
294 |
photometric := #palette.
|
|
295 |
samplesPerPixel := 1.
|
|
296 |
bitsPerSample := #(8).
|
|
297 |
|
|
298 |
"check if only grey values are used,
|
|
299 |
could make it a greyscale image if so (currently not done)"
|
|
300 |
|
83
|
301 |
"/ self checkGreyscaleColormap ifTrue:[
|
|
302 |
"/ self makeGreyscale
|
|
303 |
"/ ].
|
|
304 |
|
43
|
305 |
colorMap := Colormap redVector:redMap greenVector:greenMap blueVector:blueMap.
|
|
306 |
|
|
307 |
"
|
|
308 |
GIFReader fromFile:'../fileIn/bitmaps/claus.gif
|
|
309 |
GIFReader fromFile:'../fileIn/bitmaps/garfield.gif'
|
|
310 |
"
|
154
|
311 |
|
192
|
312 |
"Modified: 22.4.1996 / 19:11:12 / cg"
|
135
|
313 |
!
|
|
314 |
|
|
315 |
makeGreyscale
|
|
316 |
"not yet implemented/needed"
|
|
317 |
!
|
|
318 |
|
|
319 |
readColorMap:colorMapSize
|
|
320 |
"get gif colormap consisting of colorMapSize entries"
|
|
321 |
|
|
322 |
|sz "{ Class: SmallInteger }"|
|
|
323 |
|
|
324 |
"/ redMap := Array new:colorMapSize.
|
|
325 |
"/ greenMap := Array new:colorMapSize.
|
|
326 |
"/ blueMap := Array new:colorMapSize.
|
|
327 |
redMap := ByteArray uninitializedNew:colorMapSize.
|
|
328 |
greenMap := ByteArray uninitializedNew:colorMapSize.
|
|
329 |
blueMap := ByteArray uninitializedNew:colorMapSize.
|
|
330 |
sz := colorMapSize.
|
|
331 |
1 to:sz do:[:i |
|
|
332 |
redMap at:i put:(inStream nextByte).
|
|
333 |
greenMap at:i put:(inStream nextByte).
|
|
334 |
blueMap at:i put:(inStream nextByte)
|
|
335 |
]
|
154
|
336 |
!
|
|
337 |
|
|
338 |
readExtension:aStream
|
|
339 |
"get gif89 extension"
|
|
340 |
|
|
341 |
|type blockSize subBlockSize
|
|
342 |
aspNum aspDen left top width height cWidth cHeight fg bg|
|
|
343 |
|
|
344 |
type := aStream nextByte.
|
|
345 |
type == $R asciiValue ifTrue:[
|
|
346 |
"/ Ratio extension
|
|
347 |
'GIFREADER: ratio extension ignored' infoPrintNL.
|
|
348 |
blockSize := aStream nextByte.
|
|
349 |
(blockSize == 2) ifTrue:[
|
|
350 |
aspNum := aStream nextByte.
|
|
351 |
aspDen := aStream nextByte
|
|
352 |
] ifFalse:[
|
|
353 |
aStream skip:blockSize
|
|
354 |
].
|
|
355 |
"/ eat subblocks
|
|
356 |
|
|
357 |
[(subBlockSize := aStream nextByte) > 0] whileTrue:[
|
|
358 |
aStream skip:subBlockSize
|
|
359 |
]
|
|
360 |
] ifFalse:[
|
|
361 |
type == 16rFE ifTrue:[
|
|
362 |
"/ comment extension
|
|
363 |
'GIFREADER: comment extension ignored' infoPrintNL.
|
|
364 |
[(blockSize := aStream nextByte) ~~ 0] whileTrue:[
|
|
365 |
aStream skip:blockSize
|
|
366 |
].
|
|
367 |
] ifFalse:[
|
|
368 |
type == 16r01 ifTrue:[
|
|
369 |
"/ plaintext extension
|
|
370 |
'GIFREADER: plaintext extension ignored' infoPrintNL.
|
|
371 |
subBlockSize := aStream nextByte.
|
|
372 |
left := aStream nextShortMSB:false.
|
|
373 |
top := aStream nextShortMSB:false.
|
|
374 |
width := aStream nextShortMSB:false.
|
|
375 |
height := aStream nextShortMSB:false.
|
|
376 |
cWidth := aStream nextByte.
|
|
377 |
cHeight := aStream nextByte.
|
|
378 |
fg := aStream nextByte.
|
|
379 |
bg := aStream nextByte.
|
|
380 |
aStream skip:12.
|
|
381 |
[(subBlockSize := aStream nextByte) > 0] whileTrue:[
|
|
382 |
aStream skip:subBlockSize
|
|
383 |
]
|
|
384 |
] ifFalse:[
|
|
385 |
type == 16rF9 ifTrue:[
|
|
386 |
"/ graphic control extension
|
|
387 |
'GIFREADER: plaintext extension ignored' infoPrintNL.
|
|
388 |
[(subBlockSize := aStream nextByte) > 0] whileTrue:[
|
|
389 |
aStream skip:subBlockSize
|
|
390 |
]
|
|
391 |
] ifFalse:[
|
|
392 |
type == 16rFF ifTrue:[
|
|
393 |
"/ application extension
|
|
394 |
'GIFREADER: application extension ignored' infoPrintNL.
|
|
395 |
[(subBlockSize := aStream nextByte) > 0] whileTrue:[
|
|
396 |
aStream skip:subBlockSize
|
|
397 |
]
|
|
398 |
] ifFalse:[
|
|
399 |
"/ unknown extension
|
|
400 |
'GIFREADER: unknown extension ignored' infoPrintNL.
|
|
401 |
[(subBlockSize := aStream nextByte) > 0] whileTrue:[
|
|
402 |
aStream skip:subBlockSize
|
|
403 |
]
|
|
404 |
]
|
|
405 |
]
|
|
406 |
]
|
|
407 |
]
|
|
408 |
]
|
|
409 |
|
|
410 |
"Created: 18.1.1996 / 22:00:51 / cg"
|
|
411 |
"Modified: 18.1.1996 / 22:12:07 / cg"
|
0
|
412 |
! !
|
43
|
413 |
|
135
|
414 |
!GIFReader class methodsFor:'documentation'!
|
|
415 |
|
|
416 |
version
|
220
|
417 |
^ '$Header: /cvs/stx/stx/libview2/GIFReader.st,v 1.29 1996-04-25 16:23:56 cg Exp $'
|
135
|
418 |
! !
|
43
|
419 |
GIFReader initialize!
|