TIFFReader.st
changeset 3 78aaa5408119
parent 0 3f9277473954
child 5 4d55b551dc57
equal deleted inserted replaced
2:842b6a603cdc 3:78aaa5408119
    34 
    34 
    35 !TIFFReader class methodsFor:'documentation'!
    35 !TIFFReader class methodsFor:'documentation'!
    36 
    36 
    37 documentation
    37 documentation
    38 "
    38 "
    39     This class knows how to read TIFF files (and will
    39     This class knows how to read TIFF files and how to
    40     learn sometime in the future how to write them).
    40     write uncompressed TIFF files.
    41     Currently, not all formats are implemented and of
    41     Only single image files are supported.
    42     those that are, not all are tested.
    42     Currently, not all formats are implemented, and of
       
    43     those that are not all are tested.
    43     It should work with most rgb, mono and 2-plane greyscale
    44     It should work with most rgb, mono and 2-plane greyscale
    44     images, since this is what I have as test material on
    45     images, since this is what I have as test material on
    45     the NeXT.
    46     the NeXT.
    46     It supports uncompressed, LZW and G3 compressed images; 
    47     It supports reading of uncompressed, LZW and G3 compressed 
    47     JPEG is currently not implemented.
    48     images; JPEG and pacbits are currently not implemented.
    48     More formats and compressions will come ...
    49     Only writing of uncompressed images is currently implemented.
       
    50     More formats will come ...
    49 "
    51 "
    50 ! !
    52 ! !
    51 
    53 
       
    54 !TIFFReader class methodsFor:'testing'!
       
    55 
       
    56 isValidImageFile:aFileName
       
    57     "return true, if aFileName contains a GIF image"
       
    58 
       
    59     |inStream char1 char2 version|
       
    60 
       
    61     inStream := FileStream readonlyFileNamed:aFileName.
       
    62     inStream isNil ifTrue:[^ false].
       
    63 
       
    64     char1 := inStream next.
       
    65     char2 := inStream next.
       
    66 
       
    67     ((char1 ~~ char2) or:[(char1 ~~ $I) and:[char1 ~~ $M]]) ifTrue:[
       
    68         inStream close.
       
    69         ^ false
       
    70     ].
       
    71 
       
    72     inStream binary.
       
    73     version := inStream nextShortMSB:(char1 == $M).
       
    74     inStream close.
       
    75 
       
    76     (version ~~ 42) ifTrue:[^ false].
       
    77     ^ true
       
    78 ! !
       
    79 
    52 !TIFFReader methodsFor:'reading from file'!
    80 !TIFFReader methodsFor:'reading from file'!
    53 
    81 
    54 fromFile:aFileName
    82 fromFile:aFileName
       
    83     "read an image from aFileName"
       
    84 
    55     |char1 char2 version 
    85     |char1 char2 version 
    56      numberOfTags "{ Class: SmallInteger }"
    86      numberOfTags "{ Class: SmallInteger }"
    57      tagType      "{ Class: SmallInteger }"
    87      tagType      "{ Class: SmallInteger }"
    58      numberType   "{ Class: SmallInteger }"
    88      numberType   "{ Class: SmallInteger }"
    59      length       "{ Class: SmallInteger }"
    89      length       "{ Class: SmallInteger }"
    81             'not a tiff file' printNewline.
   111             'not a tiff file' printNewline.
    82             inStream close.
   112             inStream close.
    83             ^ nil
   113             ^ nil
    84         ]
   114         ]
    85     ].
   115     ].
       
   116 
       
   117     inStream binary.
    86 
   118 
    87     version := self readShort.
   119     version := self readShort.
    88     (version ~~ 42) ifTrue:[
   120     (version ~~ 42) ifTrue:[
    89         'version of tiff-file not supported' printNewline.
   121         'version of tiff-file not supported' printNewline.
    90         inStream close.
   122         inStream close.
   108 
   140 
   109     offset := self readLong + 1.
   141     offset := self readLong + 1.
   110     inStream position:offset.
   142     inStream position:offset.
   111 
   143 
   112     numberOfTags := self readShort.
   144     numberOfTags := self readShort.
   113     1 to:(numberOfTags) do:[:index |
   145     1 to:numberOfTags do:[:index |
   114         tagType := self readShort.
   146         tagType := self readShort.
   115         numberType := self readShort.
   147         numberType := self readShort.
   116         length := self readLong.
   148         length := self readLong.
   117         self decodeTiffTag:tagType numberType:numberType
   149         self decodeTiffTag:tagType numberType:numberType length:length
   118                     length:length
       
   119     ].
   150     ].
   120 
   151 
   121     offset := self readLong.
   152     offset := self readLong.
   122     (offset ~~ 0) ifTrue:[
   153     (offset ~~ 0) ifTrue:[
   123         'more tags ignored' printNewline
   154         'more tags ignored' printNewline
   124     ].
   155     ].
   125 
   156 
       
   157     "check for required tags"
   126     ok := true.
   158     ok := true.
   127     width isNil ifTrue:[
   159     width isNil ifTrue:[
   128         'missing width tag' printNewline.
   160         'missing width tag' printNewline.
   129         ok := false
   161         ok := false
   130     ].
   162     ].
   176                 result := self readPackbitsTiffImageData
   208                 result := self readPackbitsTiffImageData
   177               ] ifFalse:[
   209               ] ifFalse:[
   178                   (compression == 32865) ifTrue:[
   210                   (compression == 32865) ifTrue:[
   179                     result := self readJPEGTiffImageData
   211                     result := self readJPEGTiffImageData
   180                   ] ifFalse:[
   212                   ] ifFalse:[
   181                     'compression type not known' printNewline
   213                     'compression type ' , compression printString , ' not known' printNewline
   182                   ] 
   214                   ] 
   183               ] 
   215               ] 
   184             ] 
   216             ] 
   185           ] 
   217           ] 
   186         ] 
   218         ] 
   192 ! !
   224 ! !
   193 
   225 
   194 !TIFFReader methodsFor:'writing to file'!
   226 !TIFFReader methodsFor:'writing to file'!
   195 
   227 
   196 save:image onFile:aFileName
   228 save:image onFile:aFileName
   197     "save image as TIFF file on aFileName"
   229     "save image as (uncompressed) TIFF file on aFileName"
   198 
   230 
   199     |pos1 pos|
   231     |pos1 pos|
   200 
   232 
   201     outStream := FileStream newFileNamed:aFileName.
   233     outStream := FileStream newFileNamed:aFileName.
   202     outStream isNil ifTrue:[
   234     outStream isNil ifTrue:[
   203         'create error' printNewline. 
   235         'create error' printNewline. 
   204         ^ nil
   236         ^ nil
   205     ].
   237     ].
   206 
   238 
       
   239     "save as msb"
       
   240 
   207     byteOrder := #msb.
   241     byteOrder := #msb.
       
   242 "
       
   243     byteOrder := #lsb.
       
   244 "
   208     fillOrder := #msb.
   245     fillOrder := #msb.
   209     width := image width.
   246     width := image width.
   210     height := image height.
   247     height := image height.
   211     photometric := image photometric.
   248     photometric := image photometric.
   212     samplesPerPixel := image samplesPerPixel.
   249     samplesPerPixel := image samplesPerPixel.
   214     colorMap := image colorMap.
   251     colorMap := image colorMap.
   215     planarConfiguration := 1.
   252     planarConfiguration := 1.
   216     compression := 1.   "none"
   253     compression := 1.   "none"
   217     data := image bits.
   254     data := image bits.
   218 
   255 
   219     "save as msb"
       
   220 
   256 
   221     currentOffset := 0.
   257     currentOffset := 0.
   222 
   258 
   223     outStream nextPut:$M.
   259     (byteOrder == #msb) ifTrue:[
   224     outStream nextPut:$M.
   260         outStream nextPut:$M.
       
   261         outStream nextPut:$M.
       
   262     ] ifFalse:[
       
   263         outStream nextPut:$I.
       
   264         outStream nextPut:$I.
       
   265     ].
   225     currentOffset := currentOffset + 2.
   266     currentOffset := currentOffset + 2.
   226 
   267 
   227     outStream binary.
   268     outStream binary.
   228 
   269 
   229     self writeShort:42.         "version"
   270     self writeShort:42.
   230     currentOffset := currentOffset + 2.
   271     currentOffset := currentOffset + 2.
   231 
   272 
   232     pos1 := outStream position.
   273     pos1 := outStream position.
   233     self writeLong:0.           "start of commands - filled in later"
   274     self writeLong:0.           "start of tags - filled in later"
   234     currentOffset := currentOffset + 4.
   275     currentOffset := currentOffset + 4.
   235 
   276 
   236     "output strips"
   277     "output strips"
   237 
   278 
   238     self writeBits.             "this outputs bits as strips, sets stripOffsets and stripByteCounts"
   279     self writeBits.             "this outputs bits as strips, sets stripOffsets and stripByteCounts"
   241     self writeBitsPerSample.    "this outputs bitsPerSample, sets bitsPerSamplePos"
   282     self writeBitsPerSample.    "this outputs bitsPerSample, sets bitsPerSamplePos"
   242     photometric == #palette ifTrue:[
   283     photometric == #palette ifTrue:[
   243         self writeColorMap      "this outputs colorMap, sets colorMapPos"
   284         self writeColorMap      "this outputs colorMap, sets colorMapPos"
   244     ].
   285     ].
   245 
   286 
   246     pos := outStream position.  "backpatch tag offset"
   287     pos := outStream position.                  "backpatch tag offset"
   247     outStream position:pos1.
   288     outStream position:pos1.
   248     self writeLong:(pos - 1).
   289     self writeLong:(pos - 1).                   "fill in tag offset"
   249     outStream position:pos.
   290     outStream position:pos.
       
   291 "
   250 ('patch tag offset at: ', (pos1 printStringRadix:16) , ' to ',
   292 ('patch tag offset at: ', (pos1 printStringRadix:16) , ' to ',
   251                          (pos printStringRadix:16)) printNewline.
   293                          (pos printStringRadix:16)) printNewline.
   252 
   294 "
   253     "output tag data"
   295     "output tag data"
   254 
   296 
   255     photometric == #palette ifTrue:[
   297     photometric == #palette ifTrue:[
   256         self writeShort:9
   298         self writeShort:9.  "9 tags"
   257     ] ifFalse:[
   299     ] ifFalse:[
   258         self writeShort:8.          "8 tags"
   300         self writeShort:8.  "8 tags"
   259     ].
   301     ].
   260     self writeTag:256.               "image width"
   302     self writeTag:256.               "image width"
   261     self writeTag:257.               "image height"
   303     self writeTag:257.               "image height"
   262     self writeTag:258.               "bits per sample"
   304     self writeTag:258.               "bits per sample"
   263     self writeTag:259.               "compression"
   305     self writeTag:259.               "compression"
   266     self writeTag:279.               "strip byte counts"
   308     self writeTag:279.               "strip byte counts"
   267     self writeTag:284.               "planarconfig"
   309     self writeTag:284.               "planarconfig"
   268     photometric == #palette ifTrue:[
   310     photometric == #palette ifTrue:[
   269         self writeTag:320            "colorMap"
   311         self writeTag:320            "colorMap"
   270     ].
   312     ].
   271     self writeLong:0.
   313     self writeLong:0.                "end of tags mark"
   272 
       
   273     outStream close
       
   274 ! !
   314 ! !
   275 
   315 
   276 !TIFFReader methodsFor:'private'!
   316 !TIFFReader methodsFor:'private'!
   277 
   317 
   278 readLongs:n
   318 readLongs:n
   279     |oldPos offset values|
   319     |oldPos offset values|
   280 
   320 
   281     values := Array new:n.
   321     values := Array new:n.
   282     (n == 1) ifTrue:[
   322     (n == 1) ifTrue:[
   283         values at:1 put:(self readLong)
   323         values at:1 put:self readLong.
   284     ] ifFalse:[
   324     ] ifFalse:[
   285         offset := self readLong.
   325         offset := self readLong.
   286         oldPos := inStream position.
   326         oldPos := inStream position.
   287         inStream position:(offset + 1).
   327         inStream position:(offset + 1).
   288         1 to:n do:[:index |
   328         1 to:n do:[:index |
   289             values at:index put:(self readLong)
   329             values at:index put:self readLong
   290         ].
   330         ].
   291         inStream position:oldPos
   331         inStream position:oldPos
   292     ].
   332     ].
   293     ^ values
   333     ^ values
   294 !
   334 !
   302 readShorts:n
   342 readShorts:n
   303     |oldPos offset values|
   343     |oldPos offset values|
   304 
   344 
   305     values := Array new:n.
   345     values := Array new:n.
   306     (n <= 2) ifTrue:[
   346     (n <= 2) ifTrue:[
   307         values at:1 put:(self readShort).
   347         values at:1 put:self readShort.
   308         (n == 2) ifTrue:[
   348         (n == 2) ifTrue:[
   309             values at:2 put:(self readShort)
   349             values at:2 put:self readShort
   310         ] ifFalse:[
   350         ] ifFalse:[
   311             self readShort
   351             self readShort
   312         ]
   352         ]
   313     ] ifFalse:[
   353     ] ifFalse:[
   314         offset := self readLong.
   354         offset := self readLong.
   315         oldPos := inStream position.
   355         oldPos := inStream position.
   316         inStream position:(offset + 1).
   356         inStream position:(offset + 1).
   317         1 to:n do:[:index |
   357         1 to:n do:[:index |
   318             values at:index put:(self readShort)
   358             values at:index put:self readShort
   319         ].
   359         ].
   320         inStream position:oldPos
   360         inStream position:oldPos
   321     ].
   361     ].
   322     ^ values
   362     ^ values
   323 !
   363 !
   704         stripByteCounts := (Array new:height) atAllPut:bytesPerRow.
   744         stripByteCounts := (Array new:height) atAllPut:bytesPerRow.
   705 
   745 
   706         offs := 1.
   746         offs := 1.
   707         1 to:height do:[:row |
   747         1 to:height do:[:row |
   708             stripOffsets at:row put:(outStream position - 1).
   748             stripOffsets at:row put:(outStream position - 1).
   709             outStream nextPutBytes:data size from:data startingAt:offs.
   749             outStream nextPutBytes:bytesPerRow from:data startingAt:offs.
   710             offs := offs + bytesPerRow
   750             offs := offs + bytesPerRow
   711         ].
   751         ].
   712         rowsPerStrip := 1
   752         rowsPerStrip := 1
   713     ].
   753     ].
   714     'stripOffsets: ' print. stripOffsets printNewline.
   754     'stripOffsets: ' print. stripOffsets printNewline.
   718 writeColorMap
   758 writeColorMap
   719     colorMapPos := outStream position.
   759     colorMapPos := outStream position.
   720     colorMap do:[:subMap |
   760     colorMap do:[:subMap |
   721         subMap do:[:entry |
   761         subMap do:[:entry |
   722             "my maps are 8 bit - tiff map is 16 bit"
   762             "my maps are 8 bit - tiff map is 16 bit"
   723 
   763             entry isNil ifTrue:[
   724             self writeShort:(entry / 255 * 16rFFFF) rounded
   764                 "unused map entry"
       
   765                 self writeShort:0
       
   766             ] ifFalse:[
       
   767                 self writeShort:(entry / 255 * 16rFFFF) rounded
       
   768             ]
   725         ]
   769         ]
   726     ]
   770     ]
   727 !
   771 !
   728 
   772 
   729 writeStripOffsets
   773 writeStripOffsets
       
   774 "
   730 'stripOffsets: ' print. stripOffsets printNewline.
   775 'stripOffsets: ' print. stripOffsets printNewline.
   731 'store stripoffsets at: ' print. outStream position printNewline.
   776 'store stripoffsets at: ' print. outStream position printNewline.
       
   777 "
   732     stripOffsetsPos := outStream position.
   778     stripOffsetsPos := outStream position.
   733     stripOffsets do:[:o |
   779     stripOffsets do:[:o |
   734         self writeLong:o
   780         self writeLong:o
   735     ]
   781     ]
   736 !
   782 !
   737 
   783 
   738 writeStripByteCounts
   784 writeStripByteCounts
       
   785 "
   739 'stripByteCounts: ' print. stripByteCounts printNewline.
   786 'stripByteCounts: ' print. stripByteCounts printNewline.
   740 'store stripbytecounts at: ' print. outStream position printNewline.
   787 'store stripbytecounts at: ' print. outStream position printNewline.
       
   788 "
   741     stripByteCountsPos := outStream position.
   789     stripByteCountsPos := outStream position.
   742     stripByteCounts do:[:c |
   790     stripByteCounts do:[:c |
   743         self writeShort:c
   791         self writeShort:c
   744     ]
   792     ]
   745 !
   793 !
   746 
   794 
   747 writeBitsPerSample
   795 writeBitsPerSample
       
   796 "
   748 'bitsPerSample: ' print. bitsPerSample printNewline.
   797 'bitsPerSample: ' print. bitsPerSample printNewline.
   749 'store bitspersample at: ' print. outStream position printNewline.
   798 'store bitspersample at: ' print. outStream position printNewline.
       
   799 "
   750     bitsPerSamplePos := outStream position.
   800     bitsPerSamplePos := outStream position.
   751     bitsPerSample do:[:n |
   801     bitsPerSample do:[:n |
   752         self writeShort:n
   802         self writeShort:n
   753     ]
   803     ]
   754 !
   804 !
  1173         offset := offset + bytesPerStrip.
  1223         offset := offset + bytesPerStrip.
  1174         row := row + rowsPerStrip
  1224         row := row + rowsPerStrip
  1175     ].
  1225     ].
  1176 
  1226 
  1177     (predictor == 2) ifTrue:[
  1227     (predictor == 2) ifTrue:[
  1178 	self class decodeDelta:3 in:data width:width height:height
  1228         self class decodeDelta:3 in:data width:width height:height
  1179     ]
  1229     ]
  1180 !
  1230 !
  1181 
  1231 
  1182 readCCITTGroup3TiffImageData
  1232 readCCITTGroup3TiffImageData
  1183     "not really tested - all I got is a single
  1233     "not really tested - all I got is a single
  1214     bytesPerRow := bitsPerRow // 8.
  1264     bytesPerRow := bitsPerRow // 8.
  1215     ((bitsPerRow \\ 8) ~~ 0) ifTrue:[
  1265     ((bitsPerRow \\ 8) ~~ 0) ifTrue:[
  1216         bytesPerRow := bytesPerRow + 1
  1266         bytesPerRow := bytesPerRow + 1
  1217     ].
  1267     ].
  1218 
  1268 
  1219     data := ByteArray uninitializedNew:(bytesPerRow * height).
  1269     data := ByteArray new:(bytesPerRow * height).
  1220     compressedStrip := ByteArray uninitializedNew:bytesPerRow.
  1270     compressedStrip := ByteArray uninitializedNew:bytesPerRow.
  1221 
  1271 
  1222     offset := 1.
  1272     offset := 1.
  1223     stripNr := 0.
  1273     stripNr := 0.
  1224 
  1274 
  1225     row := 1.
  1275     row := 1.
  1226     bytesPerStrip := bytesPerRow * rowsPerStrip.
  1276     bytesPerStrip := bytesPerRow * rowsPerStrip.
  1227     [row <= height] whileTrue:[
  1277     [row <= height] whileTrue:[
  1228         stripNr := stripNr + 1.
  1278         stripNr := stripNr + 1.
  1229         inStream position:((stripOffsets at:stripNr) + 1).
  1279         inStream position:((stripOffsets at:stripNr) + 1).
  1230         inStream nextBytes:(stripByteCounts at:stripNr)
  1280         inStream nextBytes:(stripByteCounts at:stripNr) into:compressedStrip.
  1231                       into:compressedStrip.
       
  1232         self class decompressCCITT3From:compressedStrip
  1281         self class decompressCCITT3From:compressedStrip
  1233                                    into:data
  1282                                    into:data
  1234                              startingAt:offset
  1283                              startingAt:offset
  1235                                   count:width.
  1284                                   count:width.
  1236         offset := offset + bytesPerStrip.
  1285         offset := offset + bytesPerStrip.