'From Squeak3.7alpha of ''11 September 2003'' [latest update: #5566] on 24 November 2003 at 10:20:41 pm'! "Change Set: animatedGif Date: 19 November 2003 Author: Michael Rueger, Tansel Ersavas Adds support for reading and displaying animated gifs to squeak. -v2: sd remove new super initialize from AnimatedImageMorph. It would be good to have a service for fileList :), any taker... "! ImageMorph subclass: #AnimatedImageMorph instanceVariableNames: 'images offsets delays stepTime nextTime imageIndex previousOffset' classVariableNames: '' poolDictionaries: '' category: 'Morphic-Basic'! GIFReadWriter subclass: #AnimatedGIFReadWriter instanceVariableNames: 'forms offsets delays comments' classVariableNames: '' poolDictionaries: '' category: 'Graphics-Files'! !AnimatedImageMorph methodsFor: 'stepping and presenter' stamp: 'mir 11/19/2003 14:16'! step images isEmpty ifTrue: [^ self]. nextTime > Time millisecondClockValue ifTrue: [^self]. imageIndex _ imageIndex \\ images size + 1. self image: (images at: imageIndex). self position: self position + (offsets at: imageIndex) - previousOffset. previousOffset _ offsets at: imageIndex. nextTime := Time millisecondClockValue + (delays at: imageIndex) ! ! !AnimatedImageMorph methodsFor: 'stepping and presenter' stamp: 'mir 11/19/2003 13:40'! stepTime ^stepTime ifNil: [super stepTime]! ! !AnimatedImageMorph methodsFor: 'stepping and presenter' stamp: 'mir 11/19/2003 13:40'! stepTime: anInteger stepTime _ anInteger! ! !AnimatedImageMorph methodsFor: 'stepping and presenter' stamp: 'mir 11/19/2003 14:16'! wantsSteps ^ true! ! !AnimatedImageMorph methodsFor: 'private' stamp: 'mir 11/19/2003 14:14'! fromGIFFileNamed: fileName | reader | reader _ AnimatedGIFReadWriter formsFromFileNamed: fileName. images _ reader forms. offsets _ reader offsets. delays _ reader delays. self step! ! !AnimatedImageMorph methodsFor: 'private' stamp: 'mir 11/19/2003 13:42'! images ^images! ! !AnimatedImageMorph methodsFor: 'private' stamp: 'mir 11/19/2003 14:29'! initialize nextTime := Time millisecondClockValue. imageIndex := 1. previousOffset _ 0 @ 0. stepTime := 10. super initialize! ! !AnimatedImageMorph class methodsFor: 'instance creation' stamp: 'mir 11/19/2003 13:45'! fromGIFFileNamed: fileName ^self new fromGIFFileNamed: fileName! ! !GIFReadWriter methodsFor: 'private-decoding' stamp: 'mir 11/19/2003 12:19'! readBitData "using modified Lempel-Ziv Welch algorithm." | outCodes outCount bitMask initCodeSize code curCode oldCode inCode finChar i bytes f c packedBits hasLocalColor localColorSize maxOutCodes | maxOutCodes _ 4096. offset := self readWord@self readWord. "Image Left@Image Top" width _ self readWord. height _ self readWord. "--- Local Color Table Flag 1 Bit Interlace Flag 1 Bit Sort Flag 1 Bit Reserved 2 Bits Size of Local Color Table 3 Bits ----" packedBits _ self next. interlace _ (packedBits bitAnd: 16r40) ~= 0. hasLocalColor _ (packedBits bitAnd: 16r80) ~= 0. localColorSize _ 1 bitShift: ((packedBits bitAnd: 16r7) + 1). hasLocalColor ifTrue: [localColorTable _ self readColorTable: localColorSize]. pass _ 0. xpos _ 0. ypos _ 0. rowByteSize _ ((width + 3) // 4) * 4. remainBitCount _ 0. bufByte _ 0. bufStream _ ReadStream on: ByteArray new. outCodes _ ByteArray new: maxOutCodes + 1. outCount _ 0. bitMask _ (1 bitShift: bitsPerPixel) - 1. prefixTable _ Array new: 4096. suffixTable _ Array new: 4096. initCodeSize _ self next. self setParameters: initCodeSize. bitsPerPixel > 8 ifTrue: [^self error: 'never heard of a GIF that deep']. bytes _ ByteArray new: rowByteSize * height. [(code _ self readCode) = eoiCode] whileFalse: [code = clearCode ifTrue: [self setParameters: initCodeSize. curCode _ oldCode _ code _ self readCode. finChar _ curCode bitAnd: bitMask. "Horrible hack to avoid running off the end of the bitmap. Seems to cure problem reading some gifs!!? tk 6/24/97 20:16" xpos = 0 ifTrue: [ ypos < height ifTrue: [ bytes at: (ypos * rowByteSize) + xpos + 1 put: finChar]] ifFalse: [bytes at: (ypos * rowByteSize) + xpos + 1 put: finChar]. self updatePixelPosition] ifFalse: [curCode _ inCode _ code. curCode >= freeCode ifTrue: [curCode _ oldCode. outCodes at: (outCount _ outCount + 1) put: finChar]. [curCode > bitMask] whileTrue: [outCount > maxOutCodes ifTrue: [^self error: 'corrupt GIF file (OutCount)']. outCodes at: (outCount _ outCount + 1) put: (suffixTable at: curCode + 1). curCode _ prefixTable at: curCode + 1]. finChar _ curCode bitAnd: bitMask. outCodes at: (outCount _ outCount + 1) put: finChar. i _ outCount. [i > 0] whileTrue: ["self writePixel: (outCodes at: i) to: bits" bytes at: (ypos * rowByteSize) + xpos + 1 put: (outCodes at: i). self updatePixelPosition. i _ i - 1]. outCount _ 0. prefixTable at: freeCode + 1 put: oldCode. suffixTable at: freeCode + 1 put: finChar. oldCode _ inCode. freeCode _ freeCode + 1. self checkCodeSize]]. prefixTable _ suffixTable _ nil. f _ ColorForm extent: width@height depth: 8. f bits copyFromByteArray: bytes. "Squeak can handle depths 1, 2, 4, and 8" bitsPerPixel > 4 ifTrue: [^ f]. "reduce depth to save space" c _ ColorForm extent: width@height depth: (bitsPerPixel = 3 ifTrue: [4] ifFalse: [bitsPerPixel]). f displayOn: c. ^ c ! ! !GIFReadWriter methodsFor: 'private-decoding' stamp: 'mir 11/19/2003 14:29'! readBody "Read the GIF blocks. Modified to return a form. " | form extype block blocksize packedFields delay1 blockSize | form _ nil. [stream atEnd] whileFalse: [ block _ self next. block = Terminator ifTrue: [^ form]. block = ImageSeparator ifTrue: [ form isNil ifTrue: [form _ self readBitData] ifFalse: [self skipBitData]. ] ifFalse: [ block = Extension ifFalse: [^ form "^ self error: 'Unknown block type'"]. "Extension block" extype _ self next. "extension type" extype = 16rF9 ifTrue: [ "graphics control" self next = 4 ifFalse: [^ form "^ self error: 'corrupt GIF file'"]. "==== Reserved 3 Bits Disposal Method 3 Bits User Input Flag 1 Bit Transparent Color Flag 1 Bit ===" packedFields _ self next. delay1 := self next. "delay time 1" delay := (self next*256 + delay1) *10. "delay time 2" transparentIndex _ self next. (packedFields bitAnd: 1) = 0 ifTrue: [transparentIndex _ nil]. self next = 0 ifFalse: [^ form "^ self error: 'corrupt GIF file'"]. ] ifFalse: [ extype = 16rFE ifTrue: [ (blockSize _ self next) > 0 ifTrue: [comment _ (self next: blockSize) asString]] ifFalse: [ "Skip blocks" [(blocksize _ self next) > 0] whileTrue: [self next: blocksize]]]]]! ! !GIFReadWriter methodsFor: 'private' stamp: 'mir 11/19/2003 12:22'! comment: aString "Don't know what to do with it here"! ! !AnimatedGIFReadWriter methodsFor: 'accessing' stamp: 'mir 11/19/2003 13:30'! allImages | body colorTable | stream class == ReadWriteStream ifFalse: [ (stream respondsTo: #binary) ifTrue: [stream binary]. self on: (ReadWriteStream with: (stream contentsOfEntireFile))]. localColorTable _ nil. forms _ OrderedCollection new. offsets _ OrderedCollection new. delays _ OrderedCollection new. comments _ OrderedCollection new. self readHeader. [(body _ self readBody) == nil] whileFalse: [colorTable _ localColorTable ifNil: [colorPalette]. transparentIndex ifNotNil: [transparentIndex + 1 > colorTable size ifTrue: [colorTable _ colorTable forceTo: transparentIndex + 1 paddingWith: Color white]. colorTable at: transparentIndex + 1 put: Color transparent]. body colors: colorTable. forms add: body. offsets add: offset. delays add: delay]. ^ forms! ! !AnimatedGIFReadWriter methodsFor: 'accessing' stamp: 'mir 11/19/2003 14:16'! delays ^ delays! ! !AnimatedGIFReadWriter methodsFor: 'accessing' stamp: 'mir 11/19/2003 14:16'! forms ^ forms! ! !AnimatedGIFReadWriter methodsFor: 'accessing' stamp: 'mir 11/19/2003 14:16'! offsets ^ offsets! ! !AnimatedGIFReadWriter methodsFor: 'private-decoding' stamp: 'mir 11/19/2003 12:21'! readBitData | form | form := super readBitData. form offset: offset. ^form! ! !AnimatedGIFReadWriter methodsFor: 'private' stamp: 'mir 11/19/2003 12:25'! comment: aString comments add: aString! ! !AnimatedGIFReadWriter class methodsFor: 'image reading/writing' stamp: 'mir 11/18/2003 17:00'! formsFromFileNamed: fileName | stream | stream _ FileStream readOnlyFileNamed: fileName. ^ self formsFromStream: stream! ! !AnimatedGIFReadWriter class methodsFor: 'image reading/writing' stamp: 'mir 11/18/2003 17:00'! formsFromStream: stream | reader | reader _ self new on: stream reset. Cursor read showWhile: [reader allImages. reader close]. ^reader! !