GIFReader.st
author claus
Fri, 03 Jun 1994 02:54:11 +0200
changeset 21 66b31c91177f
parent 20 7fd1b1ec5f6d
child 23 11c422f6d825
permissions -rw-r--r--
*** empty log message ***

"
 COPYRIGHT (c) 1991 by Claus Gittinger
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

ImageReader subclass:#GIFReader
         instanceVariableNames:'redMap greenMap blueMap'
         classVariableNames:''
         poolDictionaries:''
         category:'Graphics-Support'
!

GIFReader comment:'
COPYRIGHT (c) 1991 by Claus Gittinger
              All Rights Reserved
'!

!GIFReader class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1991 by Claus Gittinger
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

version
"
$Header: /cvs/stx/stx/libview2/GIFReader.st,v 1.7 1994-06-03 00:52:39 claus Exp $
"
!

documentation
"
    this class provides methods for loading and saving GIF pictures.
    It has been tested with some different GIF87a pictures, I dont
    know, if it works with other GIF versions.
    GIF extension blocks are not handled.

    GIF file writing is not implemented (use TIFF).

    legal stuff extracted from GIF87a documentation:

    CompuServe Incorporated hereby grants a limited, non-exclusive, royalty-free
    license for the use of the Graphics Interchange Format(sm) in computer
    software; computer software utilizing GIF(sm) must acknowledge ownership of the
    Graphics Interchange Format and its Service Mark by CompuServe Incorporated, in
    User and Technical Documentation. 

      The Graphics Interchange Format(c) is the Copyright property of
      CompuServe Incorporated. GIF(sm) is a Service Mark property of
      CompuServe Incorporated.
"
! !

!GIFReader class methodsFor:'testing'!

isValidImageFile:aFileName
    "return true, if aFileName contains a GIF image"

    |id inStream|

    inStream := self streamReadingFile:aFileName.
    inStream isNil ifTrue:[^ false].

    id := String new:6.
    inStream nextBytes:6 into:id.
    inStream close.

    (id = 'GIF87a') ifFalse:[
        (id startsWith:'GIF') ifFalse:[^ false].

        'GIFRDR: not GIF87a - untested' errorPrintNewline.
    ].
    ^ true
! !

!GIFReader methodsFor:'reading from file'!

fromFile:aFileName
    "read a GIF file"

    |byte index flag count
     colorMapSize bitsPerPixel scrWidth scrHeight
     hasColorMap hasLocalColorMap interlaced id
     leftOffs topOffs codeLen
     compressedData compressedSize
     tmp srcOffset dstOffset|

    inStream := self class streamReadingFile:aFileName.
    inStream isNil ifTrue:[^ nil].

    inStream binary.

    "GIF-files are always lsb (intel-world)"
    byteOrder := #lsb.

    id := String new:6.
    inStream nextBytes:6 into:id.

    "all I had for testing where GIF87a files;
     I hope later versions work too ..."

    (id ~= 'GIF87a') ifTrue:[
        (id startsWith:'GIF') ifFalse:[
            'GIFRDR: not a gif file' errorPrintNewline.
            inStream close.
            ^ nil
        ].
        'GIFRDR: not a GIF87a file - hope that works' errorPrintNewline.
    ].

    "get screen dimensions (not used)"

    scrWidth := inStream nextShortMSB:false.
    scrHeight := inStream nextShortMSB:false.

    "get flag byte"
    flag := inStream nextByte.
    hasColorMap :=      (flag bitAnd:2r10000000) ~~ 0.
    "bitsPerRGB :=     ((flag bitAnd:2r01110000) bitShift:-4) + 1. "
    "colorMapSorted := ((flag bitAnd:2r00001000) ~~ 0.             "
    bitsPerPixel :=     (flag bitAnd:2r00000111) + 1.
    colorMapSize := 1 bitShift:bitsPerPixel.

    "get background (not used)"
    inStream nextByte.

    "aspect ratio (not used)"
    inStream nextByte.

    "get colorMap"
    hasColorMap ifTrue:[
        self readColorMap:colorMapSize
    ].

    "image separator"
    byte := inStream nextByte.
    (byte ~~ 16r2C) ifTrue:[
        'GIFRDR: corrupted gif file (no imgSep)' errorPrintNewline.
        ^ nil
    ].

    "get image data"
    leftOffs := inStream nextShortMSB:false.
    topOffs := inStream nextShortMSB:false.
    width := inStream nextShortMSB:false.
    height := inStream nextShortMSB:false.

"
'width ' print. width printNewline.
'height ' print. height printNewline.
"

    "another flag byte"
    flag := inStream nextByte.
    interlaced :=           (flag bitAnd:2r01000000) ~~ 0.
    hasLocalColorMap :=     (flag bitAnd:2r10000000) ~~ 0.
    "localColorMapSorted := (flag bitAnd:2r00100000) ~~ 0.      "

    "if image has a local colormap, this one is used"

    hasLocalColorMap ifTrue:[
        "local descr. overwrites"
        bitsPerPixel := (flag bitAnd:2r00000111) + 1.
        colorMapSize := 1 bitShift:bitsPerPixel.
" 'local colormap' printNewline. "
        "overwrite colormap"
        self readColorMap:colorMapSize
    ].

    "get codelen for decompression"
    codeLen := inStream nextByte.

    compressedData := ByteArray uninitializedNew:(inStream size).

    "get compressed data"
    index := 1.
    count := inStream nextByte.
    [count notNil and:[count ~~ 0]] whileTrue:[
        inStream nextBytes:count into:compressedData startingAt:index.
        index := index + count.
        count := inStream nextByte
    ].
    compressedSize := index - 1.
    inStream close.

    data := ByteArray uninitializedNew:((width + 1) * (height + 1)).
    Transcript showCr:'decompressing'.

    self class decompressGIFFrom:compressedData
                           count:compressedSize
                            into:data
                      startingAt:1
                         codeLen:(codeLen + 1).

    interlaced ifTrue:[
        Transcript showCr:'deinterlacing'.
        tmp := ByteArray uninitializedNew:(data size).

        "phase 1: 0, 8, 16, 24, ..."

        srcOffset := 1.
        0 to:(height - 1) by:8 do:[:dstRow |
            dstOffset := dstRow * width + 1.
            tmp replaceFrom:dstOffset to:(dstOffset + width - 1)
                       with:data startingAt:srcOffset.
            srcOffset := srcOffset + width.
        ].

        "phase 2: 4, 12, 20, 28, ..."

        4 to:(height - 1) by:8 do:[:dstRow |
            dstOffset := dstRow * width + 1.
            tmp replaceFrom:dstOffset to:(dstOffset + width - 1)
                       with:data startingAt:srcOffset.
            srcOffset := srcOffset + width.
        ].

        "phase 3: 2, 6, 10, 14, ..."

        2 to:(height - 1) by:4 do:[:dstRow |
            dstOffset := dstRow * width + 1.
            tmp replaceFrom:dstOffset to:(dstOffset + width - 1)
                       with:data startingAt:srcOffset.
            srcOffset := srcOffset + width.
        ].

        "phase 4: 1, 3, 5, 7, ..."

        1 to:(height - 1) by:2 do:[:dstRow |
            dstOffset := dstRow * width + 1.
            tmp replaceFrom:dstOffset to:(dstOffset + width - 1)
                       with:data startingAt:srcOffset.
            srcOffset := srcOffset + width.
        ].

        data := tmp.
        tmp := nil
    ].

    photometric := #palette.
    samplesPerPixel := 1.
    bitsPerSample := #(8).

    "check if only grey values are used,
     could make it a greyscale image if so (currently not done)"

    self checkGreyscaleColormap ifTrue:[
        self makeGreyscale
    ].
        
    colorMap := Array with:redMap with:greenMap with:blueMap.

    "GIFReader fromFile:'../fileIn/bitmaps/claus.gif"
    "GIFReader fromFile:'../fileIn/bitmaps/garfield.gif'"
!

readColorMap:colorMapSize
    "get gif colormap consisting of colorMapSize entries"

    redMap := Array new:colorMapSize.
    greenMap := Array new:colorMapSize.
    blueMap := Array new:colorMapSize.
    1 to:colorMapSize do:[:i |
        redMap at:i put:(inStream nextByte).
        greenMap at:i put:(inStream nextByte).
        blueMap at:i put:(inStream nextByte)
    ]
!

checkGreyscaleColormap
    "return true, if colormap is really a greymap"

    |sz "{ Class: SmallInteger }"
     redVal|

    sz := redMap size.

    1 to:sz do:[:i |
        redVal := redMap at:i.
        redVal ~~ (greenMap at:i) ifTrue:[^ false].
        redVal ~~ (blueMap at:i) ifTrue:[^ false].
    ].
    ^ true
!

makeGreyscale
    "not yet implemented/needed"
! !