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