added GIF writing capability
authorClaus Gittinger <cg@exept.de>
Wed, 15 Oct 1997 13:24:53 +0200
changeset 708 47d402971287
parent 707 424bb7ca69eb
child 709 813553f7bd20
added GIF writing capability
GIFReader.st
--- a/GIFReader.st	Wed Oct 15 12:55:40 1997 +0200
+++ b/GIFReader.st	Wed Oct 15 13:24:53 1997 +0200
@@ -10,9 +10,13 @@
  hereby transferred.
 "
 
+'From Smalltalk/X, Version:3.2.1 on 14-oct-1997 at 11:13:24 pm'                 !
+
 ImageReader subclass:#GIFReader
-	instanceVariableNames:'redMap greenMap blueMap'
-	classVariableNames:''
+	instanceVariableNames:'redMap greenMap blueMap pass xpos ypos rowByteSize remainBitCount
+		bufByte bufStream prefixTable suffixTable clearCode eoiCode
+		freeCode codeSize maxCode interlace'
+	classVariableNames:'ImageSeparator Extension Terminator'
 	poolDictionaries:''
 	category:'Graphics-Images-Support'
 !
@@ -71,9 +75,13 @@
     "install myself in the Image classes fileFormat table
      for the `.gif' extensions."
 
+    ImageSeparator := $, asciiValue.
+    Extension := $!! asciiValue.
+    Terminator := $; asciiValue.
+
     MIMETypes defineImageType:'image/gif' suffix:'gif' reader:self.
 
-    "Modified: 1.2.1997 / 14:59:37 / cg"
+    "Modified: 14.10.1997 / 18:47:27 / cg"
 ! !
 
 !GIFReader class methodsFor:'testing'!
@@ -104,6 +112,145 @@
     "Modified: 10.1.1997 / 15:40:34 / cg"
 ! !
 
+!GIFReader methodsFor:'private - writing'!
+
+checkCodeSize
+    (freeCode > maxCode and: [codeSize < 12])
+            ifTrue: 
+                    [codeSize := codeSize + 1.
+                    maxCode := (1 bitShift: codeSize) - 1]
+
+    "Created: 14.10.1997 / 18:42:01 / cg"
+!
+
+flushBits
+        remainBitCount = 0
+                ifFalse: 
+                        [self nextBytePut: bufByte.
+                        remainBitCount := 0].
+        self flushBuffer
+
+    "Modified: 14.10.1997 / 18:58:06 / cg"
+!
+
+flushBuffer
+    bufStream isEmpty ifTrue: [^ self].
+    outStream nextPut: bufStream size.
+    outStream nextPutAll: bufStream contents.
+    bufStream := WriteStream on: (ByteArray new: 256)
+
+    "Modified: 14.10.1997 / 20:46:04 / cg"
+!
+
+flushCode
+        self flushBits
+
+    "Created: 14.10.1997 / 18:57:33 / cg"
+!
+
+nextBitsPut: t1 
+    | t2 t3 t4 |
+    t4 := 0.
+    remainBitCount = 0
+            ifTrue: 
+                    [t3 := 8.
+                    t2 := t1]
+            ifFalse: 
+                    [t3 := remainBitCount.
+                    t2 := bufByte + (t1 bitShift: 8 - remainBitCount)].
+    [t3 < codeSize]
+            whileTrue: 
+                    [self nextBytePut: ((t2 bitShift: t4) bitAnd: 255).
+                    t4 := t4 - 8.
+                    t3 := t3 + 8].
+    (remainBitCount := t3 - codeSize) = 0
+            ifTrue: [self nextBytePut: (t2 bitShift: t4)]
+            ifFalse: [bufByte := t2 bitShift: t4].
+    ^ t1
+
+    "Modified: 14.10.1997 / 19:20:24 / cg"
+!
+
+nextBytePut: t1 
+    bufStream nextPut: t1.
+    bufStream size >= 254 ifTrue: [self flushBuffer]
+
+    "Created: 14.10.1997 / 18:40:01 / cg"
+    "Modified: 14.10.1997 / 18:40:36 / cg"
+!
+
+readPixelFrom: t1 
+    | t2 |
+    ypos >= height ifTrue: [^ nil].
+    t2 := t1 at: ypos * rowByteSize + xpos + 1.
+    self updatePixelPosition.
+    ^ t2
+
+    "Created: 14.10.1997 / 18:43:50 / cg"
+!
+
+setParameters:bitsPerPixel 
+    clearCode := 1 bitShift:bitsPerPixel.
+    eoiCode := clearCode + 1.
+    freeCode := clearCode + 2.
+    codeSize := bitsPerPixel + 1.
+    maxCode := (1 bitShift: codeSize) - 1
+
+    "Modified: 14.10.1997 / 20:09:48 / cg"
+!
+
+updatePixelPosition
+        (xpos _ xpos + 1) >= width ifFalse: [^ self].
+        xpos _ 0.
+        interlace == true
+                ifFalse: 
+                        [ypos _ ypos + 1.
+                        ^ self].
+        pass = 0
+                ifTrue: 
+                        [(ypos _ ypos + 8) >= height
+                                ifTrue: 
+                                        [pass _ pass + 1.
+                                        ypos _ 4].
+                        ^ self].
+        pass = 1
+                ifTrue: 
+                        [(ypos _ ypos + 8) >= height
+                                ifTrue: 
+                                        [pass _ pass + 1.
+                                        ypos _ 2].
+                        ^ self].
+        pass = 2
+                ifTrue: 
+                        [(ypos _ ypos + 4) >= height
+                                ifTrue: 
+                                        [pass _ pass + 1.
+                                        ypos _ 1].
+                        ^ self].
+        pass = 3
+                ifTrue: 
+                        [ypos _ ypos + 2.
+                        ^ self].
+        ^ self error: 'can''t happen'
+
+    "Modified: 14.10.1997 / 18:44:27 / cg"
+!
+
+writeCode: t1 
+    self nextBitsPut: t1
+
+    "Created: 14.10.1997 / 18:38:35 / cg"
+    "Modified: 14.10.1997 / 18:40:50 / cg"
+!
+
+writeCodeAndCheckCodeSize: t1 
+    self writeCode: t1.
+    self checkCodeSize
+
+    "Created: 14.10.1997 / 18:38:24 / cg"
+    "Modified: 14.10.1997 / 18:40:56 / cg"
+! !
+
 !GIFReader methodsFor:'reading from file'!
 
 checkGreyscaleColormap
@@ -129,7 +276,7 @@
     "read a stream containing a GIF image.
      Leave image description in instance variables."
 
-    |byte index flag count
+    |byte index flag count fileColorMap
      colorMapSize bitsPerPixel scrWidth scrHeight
      hasColorMap hasLocalColorMap interlaced id
      leftOffs topOffs codeLen
@@ -162,7 +309,6 @@
     ].
 
     "get screen dimensions (not used)"
-
     scrWidth := aStream nextShortMSB:false.
     scrHeight := aStream nextShortMSB:false.
 
@@ -183,11 +329,12 @@
     "get colorMap"
     hasColorMap ifTrue:[
         self readColorMap:colorMapSize.
-        colorMap := Colormap 
+        fileColorMap := Colormap 
                         redVector:redMap 
                         greenVector:greenMap 
                         blueVector:blueMap.
     ].
+    colorMap := fileColorMap.
 
     photometric := #palette.
     samplesPerPixel := 1.
@@ -198,15 +345,14 @@
         "gif89a extensions"
         byte := aStream nextByte.
 
-        byte == 16r21 ifTrue:[
-            "/ extension
+        byte == Extension ifTrue:[
             self readExtension:aStream.
         ] ifFalse:[
-            (byte == 16r3B) ifTrue:[ "trailer"
+            (byte == Terminator) ifTrue:[
                 atEnd := true
             ] ifFalse:[
                 "must be image separator"
-                (byte ~~ 16r2C) ifTrue:[
+                (byte ~~ ImageSeparator) ifTrue:[
                     ('GIFReader [info]: corrupted gif file (no IMAGESEP): ' , (byte printStringRadix:16)) infoPrintCR.
                     ^ nil
                 ].
@@ -244,6 +390,8 @@
                                     redVector:redMap 
                                     greenVector:greenMap 
                                     blueVector:blueMap.
+                ] ifFalse:[
+                    colorMap := fileColorMap
                 ].
 
 
@@ -344,7 +492,7 @@
     "
 
     "Modified: 5.7.1996 / 17:32:01 / stefan"
-    "Modified: 24.7.1997 / 18:02:44 / cg"
+    "Modified: 14.10.1997 / 20:45:57 / cg"
 !
 
 makeGreyscale
@@ -515,9 +663,178 @@
     "Modified: 24.7.1997 / 18:02:49 / cg"
 ! !
 
+!GIFReader methodsFor:'writing to file'!
+
+save:image onFile:aFileName
+    "save image as GIF file on aFileName"
+
+    outStream := FileStream newFileNamed:aFileName.
+    outStream isNil ifTrue:[
+        ^ Image fileCreationErrorSignal 
+            raiseWith:image
+            errorString:('file creation error: ' , aFileName asString).
+    ].
+    outStream binary.
+
+    image mask notNil ifTrue:[
+        Image informationLostQuerySignal
+            raiseWith:image
+            errorString:('GFF writer does not (yet) support an imageMask').
+    ].
+
+    byteOrder := #lsb.
+    width := image width.
+    height := image height.
+    photometric := image photometric.
+    samplesPerPixel := image samplesPerPixel.
+    bitsPerSample := image bitsPerSample.
+    colorMap := image colorMap.
+    data := image bits.
+
+    self writeHeaderFor:image.
+    self writeBitDataFor:image.
+    outStream close.
+
+    "
+     |i|
+
+     i := Image fromFile:'bitmaps/gifImages/garfield.gif'.
+     GIFReader save:i onFile:'foo.gif'.
+     (Image fromFile:'./foo.gif') inspect
+    "
+
+    "Created: 14.10.1997 / 17:40:12 / cg"
+    "Modified: 14.10.1997 / 18:59:22 / cg"
+!
+
+writeBitDataFor:image 
+    | t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t11 t12 |
+
+    t1 := image bits.
+    pass := 0.
+    xpos := 0.
+    ypos := 0.
+    rowByteSize := width * 8 + 31 // 32 * 4.
+    remainBitCount := 0.
+    bufByte := 0.
+    bufStream := WriteStream on: (ByteArray new: 256).
+    t2 := 12.
+    t3 := 1 bitShift: t2.
+    t4 := 5003.
+    prefixTable := Array new: t4.
+    suffixTable := Array new: t4.
+    t5 := image bitsPerPixel <= 1
+                            ifTrue: [2]
+                            ifFalse: [image bitsPerPixel].
+    outStream nextPut: t5.  "/ codeLen
+    self setParameters: t5.
+    t7 := 0.
+    t8 := t4.
+    [t8 < 65536] whileTrue:[ 
+        t7 := t7 + 1.
+        t8 := t8 * 2
+    ].
+    t7 := 8 - t7.
+    1 to: t4 do: [:t13 | suffixTable at: t13 put: -1].
+
+    self writeCodeAndCheckCodeSize: clearCode.
+    t6 := self readPixelFrom: t1.
+    [(t9 := self readPixelFrom: t1) == nil] whileFalse:[ 
+        t8 := (t9 bitShift: t2) + t6.
+        t10 := ((t9 bitShift: t7) bitXor: t6) + 1.
+        (suffixTable at: t10) = t8 ifTrue: [
+            t6 := prefixTable at: t10
+        ] ifFalse:[ 
+            t12 := true.
+            (suffixTable at: t10) >= 0 ifTrue:[ 
+                t11 := t4 - t10 + 1.
+                t10 = 1 ifTrue: [t11 := 1].
+
+                [
+                    (t10 := t10 - t11) < 1 ifTrue: [t10 := t10 + t4].
+                    (suffixTable at: t10) = t8 ifTrue:[ 
+                        t6 := prefixTable at: t10.
+                        t12 := false
+                    ].
+                    t12 and: [(suffixTable at: t10) > 0]
+                ] whileTrue
+            ].
+            t12 ifTrue:[
+                self writeCodeAndCheckCodeSize: t6.
+                t6 := t9.
+                freeCode < t3 ifTrue:[ 
+                    prefixTable at: t10 put: freeCode.
+                    suffixTable at: t10 put: t8.
+                    freeCode := freeCode + 1
+                ] ifFalse:[ 
+                    self writeCodeAndCheckCodeSize: clearCode.
+                    1 to: t4 do: [:t13 | suffixTable at: t13 put: -1].
+                    self setParameters: t5
+                ]
+            ]
+        ]
+    ].
+    prefixTable := suffixTable := nil.
+    self writeCodeAndCheckCodeSize: t6.
+    self writeCodeAndCheckCodeSize: eoiCode.
+    self flushCode.
+    outStream nextPut: 0.
+    outStream nextPut: Terminator
+
+    "Created: 14.10.1997 / 18:33:54 / cg"
+    "Modified: 14.10.1997 / 20:31:11 / cg"
+!
+
+writeHeaderFor:image
+    "save image as GIF file on aFileName"
+
+    |bitsPerPixel t1 n|
+
+    bitsPerPixel := image bitsPerPixel.
+
+    outStream nextPutAll: 'GIF87a' asByteArray.
+    self writeShort: width. "/ screen size
+    self writeShort: height.    
+    t1 := 128.
+    t1 := t1 bitOr: (bitsPerPixel - 1 bitShift: 5).
+    t1 := t1 bitOr: bitsPerPixel - 1.
+    outStream nextPut: t1.  "/ flag
+    outStream nextPut: 0.   "/ background (not used)
+    outStream nextPut: 0.   "/ aspect ratio
+
+    n := 0.
+    image colorMap notNil ifTrue:[
+        image colorMap do:[:clr |
+            outStream
+                nextPut: (clr redByte);
+                nextPut: (clr greenByte);
+                nextPut: (clr blueByte).
+            n := n + 1.
+        ]
+    ].
+    n+1 to:(1 bitShift:bitsPerPixel) do:[:i |
+        outStream nextPut:0; nextPut:0; nextPut:0
+    ].
+    outStream nextPut:ImageSeparator.
+    self writeShort:0.       "/
+    self writeShort:0.       "/
+    self writeShort:width.   "/ image size
+    self writeShort:height.
+
+    interlace == true ifTrue:[
+        t1 := 64
+    ] ifFalse:[
+        t1 := 0
+    ].
+    outStream nextPut:t1       "/ another flag
+
+    "Created: 14.10.1997 / 17:41:28 / cg"
+    "Modified: 14.10.1997 / 20:28:12 / cg"
+! !
+
 !GIFReader class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libview2/GIFReader.st,v 1.56 1997-09-08 18:26:48 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libview2/GIFReader.st,v 1.57 1997-10-15 11:24:53 cg Exp $'
 ! !
 GIFReader initialize!