TIFFReader.st
author Claus Gittinger <cg@exept.de>
Wed, 13 Sep 2017 10:15:34 +0200
changeset 4011 0dfddf756e27
parent 4000 d39d58494281
child 4015 7f7b255c15c2
permissions -rw-r--r--
#UI_ENHANCEMENT by cg class: TIFFReader changed: #writeTiffTag:

"
 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.
"
"{ Package: 'stx:libview2' }"

"{ NameSpace: Smalltalk }"

ImageReader subclass:#TIFFReader
	instanceVariableNames:'planarConfiguration subFileType stripOffsets rowsPerStrip
		fillOrder compression group3options predictor stripByteCounts
		currentOffset stripOffsetsPos stripByteCountsPos stripRowCounts
		bitsPerSamplePos colorMapPos orientation isBigTiff tileOffsets
		tileByteCounts tileWidth tileLength sampleFormat minSampleValue
		maxSampleValue subIfds decodeMetaTags loadFullResolutionImage
		isDNGImage'
	classVariableNames:'Verbose COMPRESSION_NONE COMPRESSION_CCITTRLE
		COMPRESSION_CCITTFAX3 COMPRESSION_CCITTFAX4 COMPRESSION_LZW
		COMPRESSION_OJPEG COMPRESSION_JPEG COMPRESSION_ADOBE_DEFLATE
		COMPRESSION_JBIG_T85 COMPRESSION_JBIG_T43 COMPRESSION_NEXT
		COMPRESSION_CCITTRLEW COMPRESSION_PACKBITS
		COMPRESSION_THUNDERSCAN COMPRESSION_IT8CTPAD COMPRESSION_IT8LW
		COMPRESSION_IT8MP COMPRESSION_IT8BL COMPRESSION_PIXARFILM
		COMPRESSION_PIXARLOG COMPRESSION_DEFLATE COMPRESSION_DCS
		COMPRESSION_JBIG COMPRESSION_JPEG2000 COMPRESSION_NIKON_NEF
		COMPRESSION_JBIG2 COMPRESSION_NEXT_JPEG SAMPLEFORMAT_UINT
		SAMPLEFORMAT_INT SAMPLEFORMAT_IEEEFP SAMPLEFORMAT_VOID
		SAMPLEFORMAT_COMPLEXINT SAMPLEFORMAT_COMPLEXIEEEFP
		COMPRESSION_SGILOG COMPRESSION_SGILOG24 PLANARCONFIG_CONTIG
		PLANARCONFIG_SEPARATE FILETYPE_MASK_REDUCEDIMAGE
		FILETYPE_MASK_PAGE FILETYPE_MASK_MASK FILLORDER_MSB2LSB
		FILLORDER_LSB2MSB'
	poolDictionaries:''
	category:'Graphics-Images-Readers'
!

Dictionary subclass:#TIFFMetaData
	instanceVariableNames:''
	classVariableNames:''
	poolDictionaries:''
	privateIn:TIFFReader
!

!TIFFReader 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.
"
!

documentation
"
    This class knows how to read TIFF files and how to write uncompressed TIFF files.

    Implemented & Missing Features:

      - Only single image files are supported.

      - Not all formats are implemented, and of those that are, not all are tested.
        It should read with most rgb, palette, mono and greyscale images, 
        although the alpha channel is currently not supported and ignored.
        It supports reading of uncompressed, LZW, packbits and CCITT-G3 compressed images
        JPEG and many other formats are currently not implemented.

      - Only writing of uncompressed images is currently implemented.
        It should write (at least) mono, 8-bit palette and 24 bit rgb formats.

      - bigTiff is supported
      
      - some dng tags are supported

    More formats will come... (will they ever be needed?)

    TODO (?): 
        since I don't want to spend all of my life adding more formats here and
        reinventing the wheel, this code should be changed to use the tiff library.
        That would give us most formats and also writing capabilities for free.

        Late note: 
            I hate C and interfacing to C libraries: it almost always leads to trouble
            w.r.t. memory leaks, non-reentrancy, non-interruptability etc.
            (we recently fixed a malloc-non-reentrant bug for some architecture...)
            So its probably better to do it all in a real programming language ;-)

    [author:]
        Claus Gittinger

    [See also:]
        Image Form Icon
        BlitImageReader FaceReader GIFReader JPEGReader PBMReader PCXReader 
        ST80FormReader SunRasterReader TargaReader WindowsIconReader 
        XBMReader XPMReader XWDReader 
"
! !

!TIFFReader class methodsFor:'initialization'!

initialize
    "install myself in the Image classes fileFormat table
     for the `.tiff' and `.tif' extensions."

    MIMETypes defineImageType:'image/tiff' suffix:'tif'  reader:self info:'tiff image'.
    MIMETypes defineImageType:nil          suffix:'tiff' reader:self.

    MIMETypes defineImageType:'image/x-adobe-dng' suffix:'dng' reader:self info:'digital negative image'.

    COMPRESSION_NONE            := 1.
    COMPRESSION_CCITTRLE        := 2.
    COMPRESSION_CCITTFAX3       := 3.
    COMPRESSION_CCITTFAX4       := 4.
    COMPRESSION_LZW             := 5.
    COMPRESSION_OJPEG           := 6. "/ (old style jpeg)
    COMPRESSION_JPEG            := 7. "/ (new style jpeg)
    COMPRESSION_ADOBE_DEFLATE   := 8.
    COMPRESSION_JBIG_T85        := 9.  "/ (ITU-T T85)
    COMPRESSION_JBIG_T43        := 10. "/ (ITU-T T43)

    COMPRESSION_NEXT            := 32766. "/ (NeXT 2-bit encoding)
    COMPRESSION_CCITTRLEW       := 32771.
    COMPRESSION_PACKBITS        := 32773.
    COMPRESSION_THUNDERSCAN     := 32809. "/ (ThunderScan 4-bit encoding)
    COMPRESSION_NEXT_JPEG       := 32865. "/ (NeXT jpeg encoding)
    COMPRESSION_IT8CTPAD        := 32895.  
    COMPRESSION_IT8LW           := 32896.
    COMPRESSION_IT8MP           := 32897. 
    COMPRESSION_IT8BL           := 32898. 
    COMPRESSION_PIXARFILM       := 32908.
    COMPRESSION_PIXARLOG        := 32909. "/ (Pixar companded 11-bit ZIP encoding)
    COMPRESSION_DEFLATE         := 32946. "/ (PKZIP-style Deflate encoding)
    COMPRESSION_DCS             := 32947. "/ (kodac)
    COMPRESSION_JBIG            := 34661.
    COMPRESSION_SGILOG          := 34676. "/ (SGI 32-bit Log Luminance encoding)
    COMPRESSION_SGILOG24        := 34677. "/ (SGI 24-bit Log Luminance encoding)
    COMPRESSION_JPEG2000        := 34712. "/ JPEG2000
    COMPRESSION_NIKON_NEF       := 34713.
    COMPRESSION_JBIG2           := 34715.
    
    SAMPLEFORMAT_UINT           := 1.       "/ !!unsigned integer data 
    SAMPLEFORMAT_INT            := 2.       "/ !!signed integer data 
    SAMPLEFORMAT_IEEEFP         := 3.       "/ !!IEEE floating point data 
    SAMPLEFORMAT_VOID           := 4.       "/ !!untyped data 
    SAMPLEFORMAT_COMPLEXINT     := 5.       "/ !!complex signed int 
    SAMPLEFORMAT_COMPLEXIEEEFP  := 6.       "/ !!complex ieee floating 

    PLANARCONFIG_CONTIG         := 1.
    PLANARCONFIG_SEPARATE       := 2.

    FILETYPE_MASK_REDUCEDIMAGE  := 1.
    FILETYPE_MASK_PAGE          := 2.
    FILETYPE_MASK_MASK          := 4. 

    FILLORDER_MSB2LSB := 1.
    FILLORDER_LSB2MSB := 2.

"/    TYPE_NOTYPE := 0.           "/ placeholder 
"/    TYPE_BYTE   := 1.           "/ 8-bit unsigned integer 
"/    TYPE_ASCII  := 2.           "/ 8-bit bytes w/ last byte null 
"/    TYPE_SHORT  := 3.           "/ 16-bit unsigned integer 
"/    TYPE_LONG   := 4.           "/ 32-bit unsigned integer 
"/    TYPE_RATIONAL := 5.         "/ 64-bit unsigned fraction 
"/    TYPE_SBYTE  := 6.           "/ !!8-bit signed integer 
"/    TYPE_UNDEFINED  := 7.       "/ !!8-bit untyped data 
"/    TYPE_SSHORT     := 8.       "/ !!16-bit signed integer 
"/    TYPE_SLONG      := 9.       "/ !!32-bit signed integer 
"/    TYPE_SRATIONAL  := 10.      "/ !!64-bit signed fraction 
"/    TYPE_FLOAT      := 11.      "/ !!32-bit IEEE floating point 
"/    TYPE_DOUBLE     := 12.      "/ !!64-bit IEEE floating point 
"/    TYPE_IFD        := 13.      "/ %32-bit unsigned integer (offset) 
"/    TYPE_LONG8      := 16.      "/ BigTIFF 64-bit unsigned integer 
"/    TYPE_SLONG8     := 17.      "/ BigTIFF 64-bit signed integer 
"/    TYPE_IFD8       := 18.      "/ BigTIFF 64-bit unsigned integer (offset) 

    "/
    "/ TIFF tags
    "/
    
"/    #define TIFFTAG_SUBFILETYPE             254     /* subfile data descriptor */
"/    #define     FILETYPE_REDUCEDIMAGE       0x1     /* reduced resolution version */
"/    #define     FILETYPE_PAGE               0x2     /* one page of many */
"/    #define     FILETYPE_MASK               0x4     /* transparency mask */
"/    #define TIFFTAG_OSUBFILETYPE            255     /* +kind of data in subfile */
"/    #define     OFILETYPE_IMAGE             1       /* full resolution image data */
"/    #define     OFILETYPE_REDUCEDIMAGE      2       /* reduced size image data */
"/    #define     OFILETYPE_PAGE              3       /* one page of many */
"/    #define TIFFTAG_IMAGEWIDTH              256     /* image width in pixels */
"/    #define TIFFTAG_IMAGELENGTH             257     /* image height in pixels */
"/    #define TIFFTAG_BITSPERSAMPLE           258     /* bits per channel (sample) */
"/    #define TIFFTAG_COMPRESSION             259     /* data compression technique */
"/    #define     COMPRESSION_NONE            1       /* dump mode */
"/    #define     COMPRESSION_CCITTRLE        2       /* CCITT modified Huffman RLE */
"/    #define     COMPRESSION_CCITTFAX3       3       /* CCITT Group 3 fax encoding */
"/    #define     COMPRESSION_CCITT_T4        3       /* CCITT T.4 (TIFF 6 name) */
"/    #define     COMPRESSION_CCITTFAX4       4       /* CCITT Group 4 fax encoding */
"/    #define     COMPRESSION_CCITT_T6        4       /* CCITT T.6 (TIFF 6 name) */
"/    #define     COMPRESSION_LZW             5       /* Lempel-Ziv  & Welch */
"/    #define     COMPRESSION_OJPEG           6       /* !!6.0 JPEG */
"/    #define     COMPRESSION_JPEG            7       /* %JPEG DCT compression */
"/    #define     COMPRESSION_T85                     9       /* !!TIFF/FX T.85 JBIG compression */
"/    #define     COMPRESSION_T43                     10      /* !!TIFF/FX T.43 colour by layered JBIG compression */
"/    #define     COMPRESSION_NEXT            32766   /* NeXT 2-bit RLE */
"/    #define     COMPRESSION_CCITTRLEW       32771   /* #1 w/ word alignment */
"/    #define     COMPRESSION_PACKBITS        32773   /* Macintosh RLE */
"/    #define     COMPRESSION_THUNDERSCAN     32809   /* ThunderScan RLE */
"/    /* codes 32895-32898 are reserved for ANSI IT8 TIFF/IT <dkelly@apago.com) */
"/    #define     COMPRESSION_IT8CTPAD        32895   /* IT8 CT w/padding */
"/    #define     COMPRESSION_IT8LW           32896   /* IT8 Linework RLE */
"/    #define     COMPRESSION_IT8MP           32897   /* IT8 Monochrome picture */
"/    #define     COMPRESSION_IT8BL           32898   /* IT8 Binary line art */
"/    /* compression codes 32908-32911 are reserved for Pixar */
"/    #define     COMPRESSION_PIXARFILM       32908   /* Pixar companded 10bit LZW */
"/    #define     COMPRESSION_PIXARLOG        32909   /* Pixar companded 11bit ZIP */
"/    #define     COMPRESSION_DEFLATE         32946   /* Deflate compression */
"/    #define     COMPRESSION_ADOBE_DEFLATE   8       /* Deflate compression,
"/                                                       as recognized by Adobe */
"/    /* compression code 32947 is reserved for Oceana Matrix <dev@oceana.com> */
"/    #define     COMPRESSION_DCS             32947   /* Kodak DCS encoding */
"/    #define     COMPRESSION_JBIG            34661   /* ISO JBIG */
"/    #define     COMPRESSION_SGILOG          34676   /* SGI Log Luminance RLE */
"/    #define     COMPRESSION_SGILOG24        34677   /* SGI Log 24-bit packed */
"/    #define     COMPRESSION_JP2000          34712   /* Leadtools JPEG2000 */
"/    #define     COMPRESSION_LZMA            34925   /* LZMA2 */
"/    #define TIFFTAG_PHOTOMETRIC             262     /* photometric interpretation */
"/    #define     PHOTOMETRIC_MINISWHITE      0       /* min value is white */
"/    #define     PHOTOMETRIC_MINISBLACK      1       /* min value is black */
"/    #define     PHOTOMETRIC_RGB             2       /* RGB color model */
"/    #define     PHOTOMETRIC_PALETTE         3       /* color map indexed */
"/    #define     PHOTOMETRIC_MASK            4       /* $holdout mask */
"/    #define     PHOTOMETRIC_SEPARATED       5       /* !!color separations */
"/    #define     PHOTOMETRIC_YCBCR           6       /* !!CCIR 601 */
"/    #define     PHOTOMETRIC_CIELAB          8       /* !!1976 CIE L*a*b* */
"/    #define     PHOTOMETRIC_ICCLAB          9       /* ICC L*a*b* [Adobe TIFF Technote 4] */
"/    #define     PHOTOMETRIC_ITULAB          10      /* ITU L*a*b* */
"/    #define     PHOTOMETRIC_CFA             32803   /* color filter array */
"/    #define     PHOTOMETRIC_LOGL            32844   /* CIE Log2(L) */
"/    #define     PHOTOMETRIC_LOGLUV          32845   /* CIE Log2(L) (u',v') */
"/    #define TIFFTAG_THRESHHOLDING           263     /* +thresholding used on data */
"/    #define     THRESHHOLD_BILEVEL          1       /* b&w art scan */
"/    #define     THRESHHOLD_HALFTONE         2       /* or dithered scan */
"/    #define     THRESHHOLD_ERRORDIFFUSE     3       /* usually floyd-steinberg */
"/    #define TIFFTAG_CELLWIDTH               264     /* +dithering matrix width */
"/    #define TIFFTAG_CELLLENGTH              265     /* +dithering matrix height */
"/    #define TIFFTAG_FILLORDER               266     /* data order within a byte */
"/    #define     FILLORDER_MSB2LSB           1       /* most significant -> least */
"/    #define     FILLORDER_LSB2MSB           2       /* least significant -> most */
"/    #define TIFFTAG_DOCUMENTNAME            269     /* name of doc. image is from */
"/    #define TIFFTAG_IMAGEDESCRIPTION        270     /* info about image */
"/    #define TIFFTAG_MAKE                    271     /* scanner manufacturer name */
"/    #define TIFFTAG_MODEL                   272     /* scanner model name/number */
"/    #define TIFFTAG_STRIPOFFSETS            273     /* offsets to data strips */
"/    #define TIFFTAG_ORIENTATION             274     /* +image orientation */
"/    #define     ORIENTATION_TOPLEFT         1       /* row 0 top, col 0 lhs */
"/    #define     ORIENTATION_TOPRIGHT        2       /* row 0 top, col 0 rhs */
"/    #define     ORIENTATION_BOTRIGHT        3       /* row 0 bottom, col 0 rhs */
"/    #define     ORIENTATION_BOTLEFT         4       /* row 0 bottom, col 0 lhs */
"/    #define     ORIENTATION_LEFTTOP         5       /* row 0 lhs, col 0 top */
"/    #define     ORIENTATION_RIGHTTOP        6       /* row 0 rhs, col 0 top */
"/    #define     ORIENTATION_RIGHTBOT        7       /* row 0 rhs, col 0 bottom */
"/    #define     ORIENTATION_LEFTBOT         8       /* row 0 lhs, col 0 bottom */
"/    #define TIFFTAG_SAMPLESPERPIXEL         277     /* samples per pixel */
"/    #define TIFFTAG_ROWSPERSTRIP            278     /* rows per strip of data */
"/    #define TIFFTAG_STRIPBYTECOUNTS         279     /* bytes counts for strips */
"/    #define TIFFTAG_MINSAMPLEVALUE          280     /* +minimum sample value */
"/    #define TIFFTAG_MAXSAMPLEVALUE          281     /* +maximum sample value */
"/    #define TIFFTAG_XRESOLUTION             282     /* pixels/resolution in x */
"/    #define TIFFTAG_YRESOLUTION             283     /* pixels/resolution in y */
"/    #define TIFFTAG_PLANARCONFIG            284     /* storage organization */
"/    #define     PLANARCONFIG_CONTIG         1       /* single image plane */
"/    #define     PLANARCONFIG_SEPARATE       2       /* separate planes of data */
"/    #define TIFFTAG_PAGENAME                285     /* page name image is from */
"/    #define TIFFTAG_XPOSITION               286     /* x page offset of image lhs */
"/    #define TIFFTAG_YPOSITION               287     /* y page offset of image lhs */
"/    #define TIFFTAG_FREEOFFSETS             288     /* +byte offset to free block */
"/    #define TIFFTAG_FREEBYTECOUNTS          289     /* +sizes of free blocks */
"/    #define TIFFTAG_GRAYRESPONSEUNIT        290     /* $gray scale curve accuracy */
"/    #define     GRAYRESPONSEUNIT_10S        1       /* tenths of a unit */
"/    #define     GRAYRESPONSEUNIT_100S       2       /* hundredths of a unit */
"/    #define     GRAYRESPONSEUNIT_1000S      3       /* thousandths of a unit */
"/    #define     GRAYRESPONSEUNIT_10000S     4       /* ten-thousandths of a unit */
"/    #define     GRAYRESPONSEUNIT_100000S    5       /* hundred-thousandths */
"/    #define TIFFTAG_GRAYRESPONSECURVE       291     /* $gray scale response curve */
"/    #define TIFFTAG_GROUP3OPTIONS           292     /* 32 flag bits */
"/    #define TIFFTAG_T4OPTIONS               292     /* TIFF 6.0 proper name alias */
"/    #define     GROUP3OPT_2DENCODING        0x1     /* 2-dimensional coding */
"/    #define     GROUP3OPT_UNCOMPRESSED      0x2     /* data not compressed */
"/    #define     GROUP3OPT_FILLBITS          0x4     /* fill to byte boundary */
"/    #define TIFFTAG_GROUP4OPTIONS           293     /* 32 flag bits */
"/    #define TIFFTAG_T6OPTIONS               293     /* TIFF 6.0 proper name */
"/    #define     GROUP4OPT_UNCOMPRESSED      0x2     /* data not compressed */
"/    #define TIFFTAG_RESOLUTIONUNIT          296     /* units of resolutions */
"/    #define     RESUNIT_NONE                1       /* no meaningful units */
"/    #define     RESUNIT_INCH                2       /* english */
"/    #define     RESUNIT_CENTIMETER          3       /* metric */
"/    #define TIFFTAG_PAGENUMBER              297     /* page numbers of multi-page */
"/    #define TIFFTAG_COLORRESPONSEUNIT       300     /* $color curve accuracy */
"/    #define     COLORRESPONSEUNIT_10S       1       /* tenths of a unit */
"/    #define     COLORRESPONSEUNIT_100S      2       /* hundredths of a unit */
"/    #define     COLORRESPONSEUNIT_1000S     3       /* thousandths of a unit */
"/    #define     COLORRESPONSEUNIT_10000S    4       /* ten-thousandths of a unit */
"/    #define     COLORRESPONSEUNIT_100000S   5       /* hundred-thousandths */
"/    #define TIFFTAG_TRANSFERFUNCTION        301     /* !!colorimetry info */
"/    #define TIFFTAG_SOFTWARE                305     /* name & release */
"/    #define TIFFTAG_DATETIME                306     /* creation date and time */
"/    #define TIFFTAG_ARTIST                  315     /* creator of image */
"/    #define TIFFTAG_HOSTCOMPUTER            316     /* machine where created */
"/    #define TIFFTAG_PREDICTOR               317     /* prediction scheme w/ LZW */
"/    #define     PREDICTOR_NONE              1       /* no prediction scheme used */
"/    #define     PREDICTOR_HORIZONTAL        2       /* horizontal differencing */
"/    #define     PREDICTOR_FLOATINGPOINT     3       /* floating point predictor */
"/    #define TIFFTAG_WHITEPOINT              318     /* image white point */
"/    #define TIFFTAG_PRIMARYCHROMATICITIES   319     /* !!primary chromaticities */
"/    #define TIFFTAG_COLORMAP                320     /* RGB map for palette image */
"/    #define TIFFTAG_HALFTONEHINTS           321     /* !!highlight+shadow info */
"/    #define TIFFTAG_TILEWIDTH               322     /* !!tile width in pixels */
"/    #define TIFFTAG_TILELENGTH              323     /* !!tile height in pixels */
"/    #define TIFFTAG_TILEOFFSETS             324     /* !!offsets to data tiles */
"/    #define TIFFTAG_TILEBYTECOUNTS          325     /* !!byte counts for tiles */
"/    #define TIFFTAG_BADFAXLINES             326     /* lines w/ wrong pixel count */
"/    #define TIFFTAG_CLEANFAXDATA            327     /* regenerated line info */
"/    #define     CLEANFAXDATA_CLEAN          0       /* no errors detected */
"/    #define     CLEANFAXDATA_REGENERATED    1       /* receiver regenerated lines */
"/    #define     CLEANFAXDATA_UNCLEAN        2       /* uncorrected errors exist */
"/    #define TIFFTAG_CONSECUTIVEBADFAXLINES  328     /* max consecutive bad lines */
"/    #define TIFFTAG_SUBIFD                  330     /* subimage descriptors */
"/    #define TIFFTAG_INKSET                  332     /* !!inks in separated image */
"/    #define     INKSET_CMYK                 1       /* !!cyan-magenta-yellow-black color */
"/    #define     INKSET_MULTIINK             2       /* !!multi-ink or hi-fi color */
"/    #define TIFFTAG_INKNAMES                333     /* !!ascii names of inks */
"/    #define TIFFTAG_NUMBEROFINKS            334     /* !!number of inks */
"/    #define TIFFTAG_DOTRANGE                336     /* !!0% and 100% dot codes */
"/    #define TIFFTAG_TARGETPRINTER           337     /* !!separation target */
"/    #define TIFFTAG_EXTRASAMPLES            338     /* !!info about extra samples */
"/    #define     EXTRASAMPLE_UNSPECIFIED     0       /* !!unspecified data */
"/    #define     EXTRASAMPLE_ASSOCALPHA      1       /* !!associated alpha data */
"/    #define     EXTRASAMPLE_UNASSALPHA      2       /* !!unassociated alpha data */
"/    #define TIFFTAG_SAMPLEFORMAT            339     /* !!data sample format */
"/    #define     SAMPLEFORMAT_UINT           1       /* !!unsigned integer data */
"/    #define     SAMPLEFORMAT_INT            2       /* !!signed integer data */
"/    #define     SAMPLEFORMAT_IEEEFP         3       /* !!IEEE floating point data */
"/    #define     SAMPLEFORMAT_VOID           4       /* !!untyped data */
"/    #define     SAMPLEFORMAT_COMPLEXINT     5       /* !!complex signed int */
"/    #define     SAMPLEFORMAT_COMPLEXIEEEFP  6       /* !!complex ieee floating */
"/    #define TIFFTAG_SMINSAMPLEVALUE         340     /* !!variable MinSampleValue */
"/    #define TIFFTAG_SMAXSAMPLEVALUE         341     /* !!variable MaxSampleValue */
"/    #define TIFFTAG_CLIPPATH                343     /* %ClipPath
"/                                                       [Adobe TIFF technote 2] */
"/    #define TIFFTAG_XCLIPPATHUNITS          344     /* %XClipPathUnits
"/                                                       [Adobe TIFF technote 2] */
"/    #define TIFFTAG_YCLIPPATHUNITS          345     /* %YClipPathUnits
"/                                                       [Adobe TIFF technote 2] */
"/    #define TIFFTAG_INDEXED                 346     /* %Indexed
"/                                                       [Adobe TIFF Technote 3] */
"/    #define TIFFTAG_JPEGTABLES              347     /* %JPEG table stream */
"/    #define TIFFTAG_OPIPROXY                351     /* %OPI Proxy [Adobe TIFF technote] */
"/    /* Tags 400-435 are from the TIFF/FX spec */
"/    #define TIFFTAG_GLOBALPARAMETERSIFD     400     /* !! */
"/    #define TIFFTAG_PROFILETYPE                     401     /* !! */
"/    #define     PROFILETYPE_UNSPECIFIED     0       /* !! */
"/    #define     PROFILETYPE_G3_FAX          1       /* !! */
"/    #define TIFFTAG_FAXPROFILE                      402     /* !! */
"/    #define     FAXPROFILE_S                        1       /* !!TIFF/FX FAX profile S */
"/    #define     FAXPROFILE_F                        2       /* !!TIFF/FX FAX profile F */
"/    #define     FAXPROFILE_J                        3       /* !!TIFF/FX FAX profile J */
"/    #define     FAXPROFILE_C                        4       /* !!TIFF/FX FAX profile C */
"/    #define     FAXPROFILE_L                        5       /* !!TIFF/FX FAX profile L */
"/    #define     FAXPROFILE_M                        6       /* !!TIFF/FX FAX profile LM */
"/    #define TIFFTAG_CODINGMETHODS           403     /* !!TIFF/FX coding methods */
"/    #define     CODINGMETHODS_T4_1D         (1 << 1)        /* !!T.4 1D */
"/    #define     CODINGMETHODS_T4_2D         (1 << 2)        /* !!T.4 2D */
"/    #define     CODINGMETHODS_T6            (1 << 3)        /* !!T.6 */
"/    #define     CODINGMETHODS_T85           (1 << 4)        /* !!T.85 JBIG */
"/    #define     CODINGMETHODS_T42           (1 << 5)        /* !!T.42 JPEG */
"/    #define     CODINGMETHODS_T43           (1 << 6)        /* !!T.43 colour by layered JBIG */
"/    #define TIFFTAG_VERSIONYEAR                     404     /* !!TIFF/FX version year */
"/    #define TIFFTAG_MODENUMBER                      405     /* !!TIFF/FX mode number */
"/    #define TIFFTAG_DECODE                          433     /* !!TIFF/FX decode */
"/    #define TIFFTAG_IMAGEBASECOLOR          434     /* !!TIFF/FX image base colour */
"/    #define TIFFTAG_T82OPTIONS                      435     /* !!TIFF/FX T.82 options */
"/    /*
"/     * Tags 512-521 are obsoleted by Technical Note #2 which specifies a
"/     * revised JPEG-in-TIFF scheme.
"/     */
"/    #define TIFFTAG_JPEGPROC                512     /* !!JPEG processing algorithm */
"/    #define     JPEGPROC_BASELINE           1       /* !!baseline sequential */
"/    #define     JPEGPROC_LOSSLESS           14      /* !!Huffman coded lossless */
"/    #define TIFFTAG_JPEGIFOFFSET            513     /* !!pointer to SOI marker */
"/    #define TIFFTAG_JPEGIFBYTECOUNT         514     /* !!JFIF stream length */
"/    #define TIFFTAG_JPEGRESTARTINTERVAL     515     /* !!restart interval length */
"/    #define TIFFTAG_JPEGLOSSLESSPREDICTORS  517     /* !!lossless proc predictor */
"/    #define TIFFTAG_JPEGPOINTTRANSFORM      518     /* !!lossless point transform */
"/    #define TIFFTAG_JPEGQTABLES             519     /* !!Q matrix offsets */
"/    #define TIFFTAG_JPEGDCTABLES            520     /* !!DCT table offsets */
"/    #define TIFFTAG_JPEGACTABLES            521     /* !!AC coefficient offsets */
"/    #define TIFFTAG_YCBCRCOEFFICIENTS       529     /* !!RGB -> YCbCr transform */
"/    #define TIFFTAG_YCBCRSUBSAMPLING        530     /* !!YCbCr subsampling factors */
"/    #define TIFFTAG_YCBCRPOSITIONING        531     /* !!subsample positioning */
"/    #define     YCBCRPOSITION_CENTERED      1       /* !!as in PostScript Level 2 */
"/    #define     YCBCRPOSITION_COSITED       2       /* !!as in CCIR 601-1 */
"/    #define TIFFTAG_REFERENCEBLACKWHITE     532     /* !!colorimetry info */
"/    #define TIFFTAG_STRIPROWCOUNTS          559 /* !!TIFF/FX strip row counts */
"/    #define TIFFTAG_XMLPACKET               700     /* %XML packet
"/                                                       [Adobe XMP Specification,
"/                                                       January 2004 */
"/    #define TIFFTAG_OPIIMAGEID              32781   /* %OPI ImageID
"/                                                       [Adobe TIFF technote] */

"/    /* tags 32952-32956 are private tags registered to Island Graphics */
"/    #define TIFFTAG_REFPTS                  32953   /* image reference points */
"/    #define TIFFTAG_REGIONTACKPOINT         32954   /* region-xform tack point */
"/    #define TIFFTAG_REGIONWARPCORNERS       32955   /* warp quadrilateral */
"/    #define TIFFTAG_REGIONAFFINE            32956   /* affine transformation mat */

"/    /* tags 32995-32999 are private tags registered to SGI */
"/    #define TIFFTAG_MATTEING                32995   /* $use ExtraSamples */
"/    #define TIFFTAG_DATATYPE                32996   /* $use SampleFormat */
"/    #define TIFFTAG_IMAGEDEPTH              32997   /* z depth of image */
"/    #define TIFFTAG_TILEDEPTH               32998   /* z depth/data tile */

"/    /* tags 33300-33309 are private tags registered to Pixar */
"/    /*
"/     * TIFFTAG_PIXAR_IMAGEFULLWIDTH and TIFFTAG_PIXAR_IMAGEFULLLENGTH
"/     * are set when an image has been cropped out of a larger image.  
"/     * They reflect the size of the original uncropped image.
"/     * The TIFFTAG_XPOSITION and TIFFTAG_YPOSITION can be used
"/     * to determine the position of the smaller image in the larger one.
"/     */
"/    #define TIFFTAG_PIXAR_IMAGEFULLWIDTH    33300   /* full image size in x */
"/    #define TIFFTAG_PIXAR_IMAGEFULLLENGTH   33301   /* full image size in y */
"/     /* Tags 33302-33306 are used to identify special image modes and data
"/      * used by Pixar's texture formats.
"/      */
"/    #define TIFFTAG_PIXAR_TEXTUREFORMAT     33302   /* texture map format */
"/    #define TIFFTAG_PIXAR_WRAPMODES         33303   /* s & t wrap modes */
"/    #define TIFFTAG_PIXAR_FOVCOT            33304   /* cotan(fov) for env. maps */
"/    #define TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN 33305
"/    #define TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA 33306

"/    /* tag 33405 is a private tag registered to Eastman Kodak */
"/    #define TIFFTAG_WRITERSERIALNUMBER      33405   /* device serial number */

"/    #define TIFFTAG_CFAREPEATPATTERNDIM     33421   /* dimensions of CFA pattern */
"/    #define TIFFTAG_CFAPATTERN              33422   /* color filter array pattern */
"/    /* tag 33432 is listed in the 6.0 spec w/ unknown ownership */
"/    #define TIFFTAG_COPYRIGHT               33432   /* copyright string */
"/    /* IPTC TAG from RichTIFF specifications */
"/    #define TIFFTAG_RICHTIFFIPTC            33723
"/    /* 34016-34029 are reserved for ANSI IT8 TIFF/IT <dkelly@apago.com) */
"/    #define TIFFTAG_IT8SITE                 34016   /* site name */
"/    #define TIFFTAG_IT8COLORSEQUENCE        34017   /* color seq. [RGB,CMYK,etc] */
"/    #define TIFFTAG_IT8HEADER               34018   /* DDES Header */
"/    #define TIFFTAG_IT8RASTERPADDING        34019   /* raster scanline padding */
"/    #define TIFFTAG_IT8BITSPERRUNLENGTH     34020   /* # of bits in short run */
"/    #define TIFFTAG_IT8BITSPEREXTENDEDRUNLENGTH 34021/* # of bits in long run */
"/    #define TIFFTAG_IT8COLORTABLE           34022   /* LW colortable */
"/    #define TIFFTAG_IT8IMAGECOLORINDICATOR  34023   /* BP/BL image color switch */
"/    #define TIFFTAG_IT8BKGCOLORINDICATOR    34024   /* BP/BL bg color switch */
"/    #define TIFFTAG_IT8IMAGECOLORVALUE      34025   /* BP/BL image color value */
"/    #define TIFFTAG_IT8BKGCOLORVALUE        34026   /* BP/BL bg color value */
"/    #define TIFFTAG_IT8PIXELINTENSITYRANGE  34027   /* MP pixel intensity value */
"/    #define TIFFTAG_IT8TRANSPARENCYINDICATOR 34028  /* HC transparency switch */
"/    #define TIFFTAG_IT8COLORCHARACTERIZATION 34029  /* color character. table */
"/    #define TIFFTAG_IT8HCUSAGE              34030   /* HC usage indicator */
"/    #define TIFFTAG_IT8TRAPINDICATOR        34031   /* Trapping indicator
"/                                                       (untrapped=0, trapped=1) */
"/    #define TIFFTAG_IT8CMYKEQUIVALENT       34032   /* CMYK color equivalents */

"/    /* tags 34232-34236 are private tags registered to Texas Instruments */
"/    #define TIFFTAG_FRAMECOUNT              34232   /* Sequence Frame Count */

"/    /* tag 34377 is private tag registered to Adobe for PhotoShop */
"/    #define TIFFTAG_PHOTOSHOP               34377 

"/    /* tags 34665, 34853 and 40965 are documented in EXIF specification */
"/    #define TIFFTAG_EXIFIFD                 34665   /* Pointer to EXIF private directory */

"/    /* tag 34750 is a private tag registered to Adobe? */
"/    #define TIFFTAG_ICCPROFILE              34675   /* ICC profile data */
"/    #define TIFFTAG_IMAGELAYER              34732   /* !!TIFF/FX image layer information */

"/    /* tag 34750 is a private tag registered to Pixel Magic */
"/    #define TIFFTAG_JBIGOPTIONS             34750   /* JBIG options */
"/    #define TIFFTAG_GPSIFD                  34853   /* Pointer to GPS private directory */

"/    /* tags 34908-34914 are private tags registered to SGI */
"/    #define TIFFTAG_FAXRECVPARAMS           34908   /* encoded Class 2 ses. parms */
"/    #define TIFFTAG_FAXSUBADDRESS           34909   /* received SubAddr string */
"/    #define TIFFTAG_FAXRECVTIME             34910   /* receive time (secs) */
"/    #define TIFFTAG_FAXDCS                  34911   /* encoded fax ses. params, Table 2/T.30 */

"/    /* tags 37439-37443 are registered to SGI <gregl@sgi.com> */
"/    #define TIFFTAG_STONITS                 37439   /* Sample value to Nits */

"/    /* tag 34929 is a private tag registered to FedEx */
"/    #define TIFFTAG_FEDEX_EDR               34929   /* unknown use */
"/    #define TIFFTAG_INTEROPERABILITYIFD     40965   /* Pointer to Interoperability private directory */

    "/
    "/ Adobe Digital Negative (DNG) format tags
    "/

"/    #define TIFFTAG_DNGVERSION              50706   /* &DNG version number */
"/    #define TIFFTAG_DNGBACKWARDVERSION      50707   /* &DNG compatibility version */
"/    #define TIFFTAG_UNIQUECAMERAMODEL       50708   /* &name for the camera model */
"/    #define TIFFTAG_LOCALIZEDCAMERAMODEL    50709   /* &localized camera model name */
"/    #define TIFFTAG_CFAPLANECOLOR           50710   /* &CFAPattern->LinearRaw space mapping */
"/    #define TIFFTAG_CFALAYOUT               50711   /* &spatial layout of the CFA */
"/    #define TIFFTAG_LINEARIZATIONTABLE      50712   /* &lookup table description */
"/    #define TIFFTAG_BLACKLEVELREPEATDIM     50713   /* &repeat pattern size for the BlackLevel tag */
"/    #define TIFFTAG_BLACKLEVEL              50714   /* &zero light encoding level */
"/    #define TIFFTAG_BLACKLEVELDELTAH        50715   /* &zero light encoding level differences (columns) */
"/    #define TIFFTAG_BLACKLEVELDELTAV        50716   /* &zero light encoding level differences (rows) */
"/    #define TIFFTAG_WHITELEVEL              50717   /* &fully saturated encoding level */
"/    #define TIFFTAG_DEFAULTSCALE            50718   /* &default scale factors */
"/    #define TIFFTAG_DEFAULTCROPORIGIN       50719   /* &origin of the final image area */
"/    #define TIFFTAG_DEFAULTCROPSIZE         50720   /* &size of the final image area */
"/    #define TIFFTAG_COLORMATRIX1            50721   /* &XYZ->reference color space transformation matrix 1 */
"/    #define TIFFTAG_COLORMATRIX2            50722   /* &XYZ->reference color space transformation matrix 2 */
"/    #define TIFFTAG_CAMERACALIBRATION1      50723   /* &calibration matrix 1 */
"/    #define TIFFTAG_CAMERACALIBRATION2      50724   /* &calibration matrix 2 */
"/    #define TIFFTAG_REDUCTIONMATRIX1        50725   /* &dimensionality reduction matrix 1 */
"/    #define TIFFTAG_REDUCTIONMATRIX2        50726   /* &dimensionality reduction matrix 2 */
"/    #define TIFFTAG_ANALOGBALANCE           50727   /* &gain applied the stored raw values*/
"/    #define TIFFTAG_ASSHOTNEUTRAL           50728   /* &selected white balance in linear reference space */
"/    #define TIFFTAG_ASSHOTWHITEXY           50729   /* &selected white balance in x-y chromaticity coordinates */
"/    #define TIFFTAG_BASELINEEXPOSURE        50730   /* &how much to move the zero point */
"/    #define TIFFTAG_BASELINENOISE           50731   /* &relative noise level */
"/    #define TIFFTAG_BASELINESHARPNESS       50732   /* &relative amount of sharpening */
"/    #define TIFFTAG_BAYERGREENSPLIT         50733   /* &how closely the values of the green pixels in the blue/green rows track the values of the green pixels in the red/green rows */
"/    #define TIFFTAG_LINEARRESPONSELIMIT     50734   /* &non-linear encoding range */
"/    #define TIFFTAG_CAMERASERIALNUMBER      50735   /* &camera's serial number */
"/    #define TIFFTAG_LENSINFO                50736   /* info about the lens */
"/    #define TIFFTAG_CHROMABLURRADIUS        50737   /* &chroma blur radius */
"/    #define TIFFTAG_ANTIALIASSTRENGTH       50738   /* &relative strength of the camera's anti-alias filter */
"/    #define TIFFTAG_SHADOWSCALE             50739   /* &used by Adobe Camera Raw */
"/    #define TIFFTAG_DNGPRIVATEDATA          50740   /* &manufacturer's private data */
"/    #define TIFFTAG_MAKERNOTESAFETY         50741   /* &whether the EXIF MakerNote tag is safe to preserve along with the rest of the EXIF data */
"/    #define TIFFTAG_CALIBRATIONILLUMINANT1  50778   /* &illuminant 1 */
"/    #define TIFFTAG_CALIBRATIONILLUMINANT2  50779   /* &illuminant 2 */
"/    #define TIFFTAG_BESTQUALITYSCALE        50780   /* &best quality multiplier */
"/    #define TIFFTAG_RAWDATAUNIQUEID         50781   /* &unique identifier for the raw image data */
"/    #define TIFFTAG_ORIGINALRAWFILENAME     50827   /* &file name of the original raw file */
"/    #define TIFFTAG_ORIGINALRAWFILEDATA     50828   /* &contents of the original raw file */
"/    #define TIFFTAG_ACTIVEAREA              50829   /* &active (non-masked) pixels of the sensor */
"/    #define TIFFTAG_MASKEDAREAS             50830   /* &list of coordinates of fully masked pixels */
"/    #define TIFFTAG_ASSHOTICCPROFILE        50831   /* &these two tags used to */
"/    #define TIFFTAG_ASSHOTPREPROFILEMATRIX  50832   /* map cameras's color space into ICC profile space */
"/    #define TIFFTAG_CURRENTICCPROFILE       50833   /* & */
"/    #define TIFFTAG_CURRENTPREPROFILEMATRIX 50834   /* & */

"/    /* tag 65535 is an undefined tag used by Eastman Kodak */
"/    #define TIFFTAG_DCSHUESHIFTVALUES       65535   /* hue shift correction data */
"/
"/    /*
"/     * The following are ``pseudo tags'' that can be used to control
"/     * codec-specific functionality.  These tags are not written to file.
"/     * Note that these values start at 0xffff+1 so that they'll never
"/     * collide with Aldus-assigned tags.
"/     *
"/     * If you want your private pseudo tags ``registered'' (i.e. added to
"/     * this file), please post a bug report via the tracking system at
"/     * http://www.remotesensing.org/libtiff/bugs.html with the appropriate
"/     * C definitions to add.
"/     */
"/    #define TIFFTAG_FAXMODE                 65536   /* Group 3/4 format control */
"/    #define     FAXMODE_CLASSIC     0x0000          /* default, include RTC */
"/    #define     FAXMODE_NORTC       0x0001          /* no RTC at end of data */
"/    #define     FAXMODE_NOEOL       0x0002          /* no EOL code at end of row */
"/    #define     FAXMODE_BYTEALIGN   0x0004          /* byte align row */
"/    #define     FAXMODE_WORDALIGN   0x0008          /* word align row */
"/    #define     FAXMODE_CLASSF      FAXMODE_NORTC   /* TIFF Class F */
"/    #define TIFFTAG_JPEGQUALITY             65537   /* Compression quality level */
"/    /* Note: quality level is on the IJG 0-100 scale.  Default value is 75 */
"/    #define TIFFTAG_JPEGCOLORMODE           65538   /* Auto RGB<=>YCbCr convert? */
"/    #define     JPEGCOLORMODE_RAW   0x0000          /* no conversion (default) */
"/    #define     JPEGCOLORMODE_RGB   0x0001          /* do auto conversion */
"/    #define TIFFTAG_JPEGTABLESMODE          65539   /* What to put in JPEGTables */
"/    #define     JPEGTABLESMODE_QUANT 0x0001         /* include quantization tbls */
"/    #define     JPEGTABLESMODE_HUFF 0x0002          /* include Huffman tbls */
"/    /* Note: default is JPEGTABLESMODE_QUANT | JPEGTABLESMODE_HUFF */
"/    #define TIFFTAG_FAXFILLFUNC             65540   /* G3/G4 fill function */
"/    #define TIFFTAG_PIXARLOGDATAFMT         65549   /* PixarLogCodec I/O data sz */
"/    #define     PIXARLOGDATAFMT_8BIT        0       /* regular u_char samples */
"/    #define     PIXARLOGDATAFMT_8BITABGR    1       /* ABGR-order u_chars */
"/    #define     PIXARLOGDATAFMT_11BITLOG    2       /* 11-bit log-encoded (raw) */
"/    #define     PIXARLOGDATAFMT_12BITPICIO  3       /* as per PICIO (1.0==2048) */
"/    #define     PIXARLOGDATAFMT_16BIT       4       /* signed short samples */
"/    #define     PIXARLOGDATAFMT_FLOAT       5       /* IEEE float samples */
"/    /* 65550-65556 are allocated to Oceana Matrix <dev@oceana.com> */
"/    #define TIFFTAG_DCSIMAGERTYPE           65550   /* imager model & filter */
"/    #define     DCSIMAGERMODEL_M3           0       /* M3 chip (1280 x 1024) */
"/    #define     DCSIMAGERMODEL_M5           1       /* M5 chip (1536 x 1024) */
"/    #define     DCSIMAGERMODEL_M6           2       /* M6 chip (3072 x 2048) */
"/    #define     DCSIMAGERFILTER_IR          0       /* infrared filter */
"/    #define     DCSIMAGERFILTER_MONO        1       /* monochrome filter */
"/    #define     DCSIMAGERFILTER_CFA         2       /* color filter array */
"/    #define     DCSIMAGERFILTER_OTHER       3       /* other filter */
"/    #define TIFFTAG_DCSINTERPMODE           65551   /* interpolation mode */
"/    #define     DCSINTERPMODE_NORMAL        0x0     /* whole image, default */
"/    #define     DCSINTERPMODE_PREVIEW       0x1     /* preview of image (384x256) */
"/    #define TIFFTAG_DCSBALANCEARRAY         65552   /* color balance values */
"/    #define TIFFTAG_DCSCORRECTMATRIX        65553   /* color correction values */
"/    #define TIFFTAG_DCSGAMMA                65554   /* gamma value */
"/    #define TIFFTAG_DCSTOESHOULDERPTS       65555   /* toe & shoulder points */
"/    #define TIFFTAG_DCSCALIBRATIONFD        65556   /* calibration file desc */
"/    /* Note: quality level is on the ZLIB 1-9 scale. Default value is -1 */
"/    #define TIFFTAG_ZIPQUALITY              65557   /* compression quality level */
"/    #define TIFFTAG_PIXARLOGQUALITY         65558   /* PixarLog uses same scale */
"/    /* 65559 is allocated to Oceana Matrix <dev@oceana.com> */
"/    #define TIFFTAG_DCSCLIPRECTANGLE        65559   /* area of image to acquire */
"/    #define TIFFTAG_SGILOGDATAFMT           65560   /* SGILog user data format */
"/    #define     SGILOGDATAFMT_FLOAT         0       /* IEEE float samples */
"/    #define     SGILOGDATAFMT_16BIT         1       /* 16-bit samples */
"/    #define     SGILOGDATAFMT_RAW           2       /* uninterpreted data */
"/    #define     SGILOGDATAFMT_8BIT          3       /* 8-bit RGB monitor values */
"/    #define TIFFTAG_SGILOGENCODE            65561 /* SGILog data encoding control*/
"/    #define     SGILOGENCODE_NODITHER       0     /* do not dither encoded values*/
"/    #define     SGILOGENCODE_RANDITHER      1     /* randomly dither encd values */
"/    #define TIFFTAG_LZMAPRESET              65562   /* LZMA2 preset (compression level) */
"/    #define TIFFTAG_PERSAMPLE       65563   /* interface for per sample tags */
"/    #define     PERSAMPLE_MERGED        0   /* present as a single value */
"/    #define     PERSAMPLE_MULTI         1   /* present as multiple values */

    "/
    "/ EXIF tags
    "/
"/    #define EXIFTAG_EXPOSURETIME            33434   /* Exposure time */
"/    #define EXIFTAG_FNUMBER                 33437   /* F number */
"/    #define EXIFTAG_EXPOSUREPROGRAM         34850   /* Exposure program */
"/    #define EXIFTAG_SPECTRALSENSITIVITY     34852   /* Spectral sensitivity */
"/    #define EXIFTAG_ISOSPEEDRATINGS         34855   /* ISO speed rating */
"/    #define EXIFTAG_OECF                    34856   /* Optoelectric conversion factor */
"/    #define EXIFTAG_EXIFVERSION             36864   /* Exif version */
"/    #define EXIFTAG_DATETIMEORIGINAL        36867   /* Date and time of original data generation */
"/    #define EXIFTAG_DATETIMEDIGITIZED       36868   /* Date and time of digital data generation */
"/    #define EXIFTAG_COMPONENTSCONFIGURATION 37121   /* Meaning of each component */
"/    #define EXIFTAG_COMPRESSEDBITSPERPIXEL  37122   /* Image compression mode */
"/    #define EXIFTAG_SHUTTERSPEEDVALUE       37377   /* Shutter speed */
"/    #define EXIFTAG_APERTUREVALUE           37378   /* Aperture */
"/    #define EXIFTAG_BRIGHTNESSVALUE         37379   /* Brightness */
"/    #define EXIFTAG_EXPOSUREBIASVALUE       37380   /* Exposure bias */
"/    #define EXIFTAG_MAXAPERTUREVALUE        37381   /* Maximum lens aperture */
"/    #define EXIFTAG_SUBJECTDISTANCE         37382   /* Subject distance */
"/    #define EXIFTAG_METERINGMODE            37383   /* Metering mode */
"/    #define EXIFTAG_LIGHTSOURCE             37384   /* Light source */
"/    #define EXIFTAG_FLASH                   37385   /* Flash */
"/    #define EXIFTAG_FOCALLENGTH             37386   /* Lens focal length */
"/    #define EXIFTAG_SUBJECTAREA             37396   /* Subject area */
"/    #define EXIFTAG_MAKERNOTE               37500   /* Manufacturer notes */
"/    #define EXIFTAG_USERCOMMENT             37510   /* User comments */
"/    #define EXIFTAG_SUBSECTIME              37520   /* DateTime subseconds */
"/    #define EXIFTAG_SUBSECTIMEORIGINAL      37521   /* DateTimeOriginal subseconds */
"/    #define EXIFTAG_SUBSECTIMEDIGITIZED     37522   /* DateTimeDigitized subseconds */
"/    #define EXIFTAG_FLASHPIXVERSION         40960   /* Supported Flashpix version */
"/    #define EXIFTAG_COLORSPACE              40961   /* Color space information */
"/    #define EXIFTAG_PIXELXDIMENSION         40962   /* Valid image width */
"/    #define EXIFTAG_PIXELYDIMENSION         40963   /* Valid image height */
"/    #define EXIFTAG_RELATEDSOUNDFILE        40964   /* Related audio file */
"/    #define EXIFTAG_FLASHENERGY             41483   /* Flash energy */
"/    #define EXIFTAG_SPATIALFREQUENCYRESPONSE 41484  /* Spatial frequency response */
"/    #define EXIFTAG_FOCALPLANEXRESOLUTION   41486   /* Focal plane X resolution */
"/    #define EXIFTAG_FOCALPLANEYRESOLUTION   41487   /* Focal plane Y resolution */
"/    #define EXIFTAG_FOCALPLANERESOLUTIONUNIT 41488  /* Focal plane resolution unit */
"/    #define EXIFTAG_SUBJECTLOCATION         41492   /* Subject location */
"/    #define EXIFTAG_EXPOSUREINDEX           41493   /* Exposure index */
"/    #define EXIFTAG_SENSINGMETHOD           41495   /* Sensing method */
"/    #define EXIFTAG_FILESOURCE              41728   /* File source */
"/    #define EXIFTAG_SCENETYPE               41729   /* Scene type */
"/    #define EXIFTAG_CFAPATTERN              41730   /* CFA pattern */
"/    #define EXIFTAG_CUSTOMRENDERED          41985   /* Custom image processing */
"/    #define EXIFTAG_EXPOSUREMODE            41986   /* Exposure mode */
"/    #define EXIFTAG_WHITEBALANCE            41987   /* White balance */
"/    #define EXIFTAG_DIGITALZOOMRATIO        41988   /* Digital zoom ratio */
"/    #define EXIFTAG_FOCALLENGTHIN35MMFILM   41989   /* Focal length in 35 mm film */
"/    #define EXIFTAG_SCENECAPTURETYPE        41990   /* Scene capture type */
"/    #define EXIFTAG_GAINCONTROL             41991   /* Gain control */
"/    #define EXIFTAG_CONTRAST                41992   /* Contrast */
"/    #define EXIFTAG_SATURATION              41993   /* Saturation */
"/    #define EXIFTAG_SHARPNESS               41994   /* Sharpness */
"/    #define EXIFTAG_DEVICESETTINGDESCRIPTION 41995  /* Device settings description */
"/    #define EXIFTAG_SUBJECTDISTANCERANGE    41996   /* Subject distance range */
"/    #define EXIFTAG_GAINCONTROL             41991   /* Gain control */
"/    #define EXIFTAG_GAINCONTROL             41991   /* Gain control */
"/    #define EXIFTAG_IMAGEUNIQUEID           42016   /* Unique image ID */

    "
     self initialize
    "

    "Modified: / 28-08-2017 / 00:46:15 / cg"
! !

!TIFFReader class methodsFor:'testing'!

canRepresent:anImage
    "return true, if anImage can be represented in my file format.
     Any image is supported."

    ^ true
!

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

    |inStream bytes1_2 byte3 byte4 versionLow versionHi|

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

    bytes1_2 := inStream next:2.
    byte3 := inStream nextByte.
    byte4 := inStream nextByte.
    inStream close.
    
    ((bytes1_2 = 'II' "tiff") or:[bytes1_2 = 'PE' "mdi"]) ifTrue:[
        "/ intel byte order (lsb)
        versionLow := byte3. versionHi := byte4.
    ] ifFalse:[
        ((bytes1_2 = 'MM' "tiff") or:[bytes1_2 = 'EP' "mdi"])  ifTrue:[
            "/ motorola byte order (msb)
            versionLow := byte4. versionHi := byte3.
        ] ifFalse:[
            ^ false
        ].    
    ].
    versionHi == 0 ifTrue:[
        (versionLow == 42) ifTrue:[^ true].       "/ tiff
        (versionLow == 43) ifTrue:[^ true].       "/ bigtiff
    ].
    ^ false.

    "Modified: / 26-08-2017 / 15:31:08 / cg"
! !

!TIFFReader methodsFor:'accessing'!

decodeMetaTags:aBoolean
    "by default, extra meta tags (photoshop) are not decoded;
     set to true to get them in metatags"
     
    decodeMetaTags := aBoolean.

    "Modified (comment): / 27-08-2017 / 18:37:04 / cg"
!

loadFullResolutionImage:aBoolean
    "by default, the full-resolution dng image is not decoded;
     set to true to get it in the imageSequence"
     
    loadFullResolutionImage := aBoolean.

    "Created: / 27-08-2017 / 18:41:48 / cg"
! !

!TIFFReader methodsFor:'private-data reading'!

readAdobeDeflateTiffImageData
    ^ self readDeflateTiffImageData

    "Created: / 26-08-2017 / 14:44:35 / cg"
    "Modified: / 27-08-2017 / 18:55:11 / cg"
!

readCCITT3RLETiffImageData
    ^ self fileFormatError:'ccitt G3 mod Huffman (rle) compression not implemented'

    "Modified: / 3.2.1998 / 18:03:14 / cg"
!

readCCITT3RLEWTiffImageData
    ^ self fileFormatError:'ccitt G3 mod Huffman (rlew) compression not implemented'.

    "Modified: / 3.2.1998 / 18:03:30 / cg"
!

readCCITTGroup3TiffImageData
    "not really tested - all I got is a single fax from NeXT step"

    |bytesPerRow bitsPerRow compressedStrip nPlanes 
     stripNr       "{ Class: SmallInteger }"
     offset        "{ Class: SmallInteger }"
     row           "{ Class: SmallInteger }"
     bytesPerStrip "{ Class: SmallInteger }" 
     count dstIndex|

    nPlanes := samplesPerPixel.
    (nPlanes ~~ 1) ifTrue:[
        (nPlanes == 2) ifTrue:[
            (planarConfiguration ~~ PLANARCONFIG_SEPARATE) ifTrue:[
                ^ self fileFormatError:'only separate planes are supported'.
            ].
            'TIFFReader [info]: ignoring alpha plane' infoPrintCR.
            nPlanes := 1
        ] ifFalse:[
            ^ self fileFormatError:'only monochrome/greyscale ccitt3supported'.
        ].   
    ].
    stripRowCounts notNil ifTrue:[
        ^ self fileFormatError:'stripRowCounts not supported'.
    ].

"/    (rowsPerStrip ~~ 1) ifTrue:[
"/        ^ self fileFormatError:'currently require rowsPerStrip to be 1'.
"/    ].

    "/ 'TIFFReader: decompressing CCITT-3 ...' infoPrintNL.

    bitsPerRow := width * (bitsPerSample at:1).
    bytesPerRow := bitsPerRow // 8.
    ((bitsPerRow \\ 8) ~~ 0) ifTrue:[
        bytesPerRow := bytesPerRow + 1
    ].

    data := ByteArray new:(bytesPerRow * height).

    "/ if the number of rows per strip is unknown (-1),
    "/ make it one big strip and decompress that
    rowsPerStrip = 16rFFFFFFFF ifTrue:[
        self assert:(stripByteCounts size == 1).
        count := stripByteCounts sum.
        compressedStrip := ByteArray uninitializedNew:count.
        self positionToStrip:1.

        (inStream nextBytes:count into:compressedStrip) == count ifFalse:[
            self error:'short read'
        ].

        compressedStrip := compressedStrip copyFrom:16r227.
        
        dstIndex := self class 
                    _decompressCCITT3From:compressedStrip
                    count:count
                    into:data
                    startingAt:1.
        ^ self 
    ].
    
    compressedStrip := ByteArray uninitializedNew:bytesPerRow.

    offset := 1.
    stripNr := 1.

    row := 1.
    bytesPerStrip := bytesPerRow * rowsPerStrip.
    [row <= height] whileTrue:[
        self positionToStrip:stripNr.
        count := stripByteCounts at:stripNr.
        (inStream nextBytes:count into:compressedStrip) == count ifFalse:[
            self error:'short read'
        ].    
        dstIndex := self class 
                    _decompressCCITT3From:compressedStrip
                    count:count
                    into:data
                    startingAt:offset
                    count:width. "/ is this correct? - I think, that should be width*rowsPerStrip 
        offset := offset + bytesPerStrip.
        row := row + rowsPerStrip.
        stripNr := stripNr + 1.
    ]

    "
     TIFFReader fromFile:'/Users/cg/DownloadsUnsaved/image_software/libtiffpic/g3test.tif'
    "

    "Modified: / 27-08-2017 / 15:37:51 / cg"
!

readCCITTGroup4TiffImageData
    ^ self fileFormatError:'ccitt group4 fax compression not implemented'.

    "Modified: / 3.2.1998 / 18:04:34 / cg"
!

readCCITTRLEWTiffImageData
    ^ self fileFormatError:'ccitt mod Huffman (rlew) compression not implemented'
!

readDCSTiffImageData
    ^ self fileFormatError:'dcs compression not implemented'.

    "Modified: / 3.2.1998 / 18:04:44 / cg"
!

readDeflateTiffImageData
    self readImageDataUsingDecompressor:
        [:inBytes :inCount :outBytes :outOffset :outCount |
            |zlibReader|
            
            zlibReader := ZipStream readOpenAsZipStreamOn:(inBytes readStream) suppressHeaderAndChecksum:false.
            zlibReader binary.
            zlibReader next:outCount into:outBytes startingAt:outOffset.
            outCount.
        ].

"/    |bytesPerRowIn bytesPerRow nPlanes overAllBytes
"/     bytesPerStrip "{ Class: SmallInteger }"
"/     nBytes        "{ Class: SmallInteger }"
"/     stripNr       "{ Class: SmallInteger }"
"/     offset        "{ Class: SmallInteger }"
"/     row           "{ Class: SmallInteger }" 
"/     zlibReader nread msb
"/     convertFloats convertDoubles conversionBuffer|
"/
"/    nPlanes := samplesPerPixel.
"/    convertFloats := convertDoubles := false.
"/    
"/    (nPlanes >= 3) ifTrue:[
"/        bytesPerRowIn := width * ((bitsPerSample sum + 7) // 8).
"/        (bitsPerSample conform:[:each | each == 8]) ifTrue:[
"/            sampleFormat == SAMPLEFORMAT_UINT ifFalse:[
"/                ^ self fileFormatError:'unsupported sample format'.
"/            ].
"/        ] ifFalse:[
"/            sampleFormat == SAMPLEFORMAT_IEEEFP ifTrue:[
"/                nPlanes == 3 ifTrue:[
"/                    (bitsPerSample conform:[:each | each == 64]) ifTrue:[
"/                        convertDoubles := true.
"/                        bytesPerRow := width * nPlanes.
"/                        bitsPerSample := #(8 8 8).
"/                    ] ifFalse:[
"/                        (bitsPerSample conform:[:each | each == 32]) ifTrue:[
"/                            convertFloats := true.
"/                            bytesPerRow := width * nPlanes.
"/                            bitsPerSample := #(8 8 8).
"/                        ] ifFalse:[
"/                            ^ self fileFormatError:'only 64/64/64 bits/sample are supported with IEEE_FP samples'.
"/                        ].    
"/                    ].    
"/                ] ifFalse:[
"/                    ^ self fileFormatError:'only support 3 planes with IEEE_FP sample format'.
"/                ]    
"/            ] ifFalse:[    
"/                ^ self fileFormatError:'unsupported sample format'.
"/            ].
"/        ]
"/    ] ifFalse:[
"/        (nPlanes == 2) ifTrue:[
"/            (planarConfiguration ~~ PLANARCONFIG_SEPARATE) ifTrue:[
"/                ^ self fileFormatError:'only separate planes are supported'.
"/            ].
"/            'TIFFReader [info]: ignoring alpha plane' infoPrintCR.
"/            nPlanes := 1
"/        ].
"/        (nPlanes == 1) ifFalse:[
"/            ^ self fileFormatError:'unsupported nPlanes: ' , nPlanes printString, '; only 3-sample rgb / monochrome supported'.
"/        ].
"/        bytesPerRowIn := (width * (bitsPerSample at:1) + 7) // 8.
"/    ].
"/    stripRowCounts notNil ifTrue:[
"/        ^ self fileFormatError:'stripRowCounts not supported'.
"/    ].
"/
"/    "/ 'TIFFReader: decompressing Deflate ...' infoPrintNL.
"/
"/    bytesPerRow isNil ifTrue:[ bytesPerRow := bytesPerRowIn ].
"/    
"/    overAllBytes := bytesPerRow * height.
"/    data := ByteArray new:overAllBytes.
"/    (convertFloats or:[convertDoubles]) ifTrue:[
"/        conversionBuffer := ByteArray new:(bytesPerRowIn * rowsPerStrip).
"/    ].    
"/    
"/    offset := 1.
"/    stripNr := 0.
"/
"/    row := 1.
"/    bytesPerStrip := bytesPerRow * rowsPerStrip.
"/
"/    [row <= height] whileTrue:[
"/        stripNr := stripNr + 1.
"/        self positionToStrip:stripNr.
"/        nBytes := stripByteCounts at:stripNr.
"/        
"/        zlibReader := ZipStream readOpenAsZipStreamOn:inStream suppressHeaderAndChecksum:false.
"/        zlibReader binary.
"/
"/        conversionBuffer notNil ifTrue:[
"/            nread := zlibReader next:nBytes into:conversionBuffer startingAt:1.
"/            msb := (byteOrder == #msb).
"/            convertFloats ifTrue:[
"/                |i|
"/
"/                self assert:(nread \\ 4) == 0.
"/                i := 0.
"/                1 to:nread-1 by:4 do:[:iF |
"/                    |dVal byteVal|
"/                    dVal := conversionBuffer floatAt:iF MSB:msb.
"/                    "/ rescale from 0..1 to 0..255
"/                    byteVal := (dVal * 255) asInteger clampBetween:0 and:255.
"/                    data at:offset+i put:byteVal.
"/                    i := i + 1.
"/                ].
"/            ] ifFalse:[
"/                convertDoubles ifTrue:[
"/                    |i|
"/                    
"/                    self assert:(nread \\ 8) == 0.
"/                    i := 0.
"/                    1 to:nread-1 by:8 do:[:iF |
"/                        |dVal byteVal|
"/                        dVal := conversionBuffer doubleAt:iF MSB:msb.
"/                        "/ rescale from 0..1 to 0..255
"/                        byteVal := (dVal * 255) asInteger clampBetween:0 and:255.
"/                        data at:offset+i put:byteVal.
"/                        i := i + 1.
"/                    ].
"/                ]
"/            ].    
"/        ] ifFalse:[    
"/            nread := zlibReader next:nBytes into:data startingAt:offset.
"/        ].
"/        
"/        offset := offset + bytesPerStrip.
"/        row := row + rowsPerStrip
"/    ].
"/
"/    (predictor ~~ 1) ifTrue:[
"/        (predictor == 2) ifTrue:[
"/            self class decodeDelta:nPlanes in:data width:width height:height
"/        ] ifFalse:[
"/            ^ self fileFormatError:'unsupported predictor'
"/        ].    
"/    ]

    "Modified: / 29-08-2017 / 23:14:55 / cg"
!

readImageDataUsingDecompressor:decompressorBlock
    |bytesPerRowIn bytesPerRow nPlanes overAllBytes
     nBytes        "{ Class: SmallInteger }"
     stripNr       "{ Class: SmallInteger }"
     offset        "{ Class: SmallInteger }"
     row           "{ Class: SmallInteger }" 
     msb
     convert convertFloats convertDoubles convertHalfFloats 
     swapInt16s convertFillOrder
     inBuffer conversionBuffer floats nDecompressed 
     rowsInThisStrip bytesInThisStrip|

    nPlanes := samplesPerPixel.
    convert := convertFloats := convertDoubles := convertHalfFloats := false.
    swapInt16s := convertFillOrder := false.
    
    (nPlanes >= 3) ifTrue:[
        (planarConfiguration ~~ PLANARCONFIG_CONTIG) ifTrue:[
            ^ self fileFormatError:('only non separate planes are supported with %1 planes' bindWith:nPlanes).
        ].
        (nPlanes > 4) ifTrue:[
            photometric == #cmyk ifTrue:[
                "/ ignore alpha
                'TIFFReader [info]: ignoring alpha plane' infoPrintCR.
                nPlanes := samplesPerPixel := 4.
                bitsPerSample := bitsPerSample copyTo:4.
            ].
        ].
        
        bytesPerRowIn := width * ((bitsPerSample sum + 7) // 8).
        sampleFormat == SAMPLEFORMAT_UINT ifTrue:[
            (bitsPerSample conform:[:each | each == 8]) ifTrue:[
                "/ ok
            ] ifFalse:[
                (bitsPerSample conform:[:each | each == 16]) ifTrue:[
                    "/ ok
                ] ifFalse:[
                    ^ self fileFormatError:('%1 bits/sample; only 8 or 16 are supported with %2 planes' bindWith:bitsPerSample with:nPlanes).
                ]
            ].    
        ] ifFalse:[
            sampleFormat == SAMPLEFORMAT_IEEEFP ifTrue:[
                nPlanes == 3 ifTrue:[
                    (bitsPerSample conform:[:each | each == 64]) ifTrue:[
                        convertDoubles := convert := true.
                        bytesPerRow := width * nPlanes.
                        bitsPerSample := #(8 8 8).
                    ] ifFalse:[
                        (bitsPerSample conform:[:each | each == 32]) ifTrue:[
                            convertFloats := convert := true.
                            bytesPerRow := width * nPlanes.
                            bitsPerSample := #(8 8 8).
                        ] ifFalse:[
                            (bitsPerSample conform:[:each | each == 16]) ifTrue:[
                                convertHalfFloats := convert := true.
                                bytesPerRow := width * nPlanes.
                                bitsPerSample := #(8 8 8).
                            ] ifFalse:[
                                ^ self fileFormatError:'only 16, 32 or 64 bits/sample are supported with IEEE_FP samples'.
                            ].    
                        ].    
                    ].    
                ] ifFalse:[
                    ^ self fileFormatError:'only support 3 planes with IEEE_FP sample format'.
                ]    
            ] ifFalse:[    
                ^ self fileFormatError:'unsupported sample format'.
            ].
        ]
    ] ifFalse:[
        (nPlanes == 2) ifTrue:[
            (planarConfiguration ~~ PLANARCONFIG_SEPARATE) ifTrue:[
                ^ self fileFormatError:'only separate planes are supported with 2 planes'.
            ].
            'TIFFReader [info]: ignoring alpha plane' infoPrintCR.
            nPlanes := 1
        ].
        (nPlanes == 1) ifFalse:[
            ^ self fileFormatError:'unsupported nPlanes: ' , nPlanes printString, '; only 3-sample rgb / monochrome supported'.
        ].
        bytesPerRowIn := (width * (bitsPerSample at:1) + 7) // 8.
        (bitsPerSample at:1) == 16 ifTrue:[
            byteOrder ~~ #msb ifTrue:[
                "/ swapInt16s := true.
            ].    
        ].    
    ].

    bitsPerSample sum isPowerOfTwo ifFalse:[
        (#(24 48) includes:bitsPerSample sum) ifFalse:[
            ^ self fileFormatError:'unsupported bitsPerSample: ' , bitsPerSample printString
        ].
    ].
    
    fillOrder ~~ #msb ifTrue:[
        (bitsPerSample conform:[:n | n == 8]) ifFalse:[
            ^ self fileFormatError:'unsupported bitsPerSample with lsb fillOrder'
        ].
        convertFillOrder := true.
    ].
    
    bytesPerRow isNil ifTrue:[ bytesPerRow := bytesPerRowIn ].
    
    overAllBytes := bytesPerRow * height.
    data := ByteArray new:overAllBytes.
    convert ifTrue:[
        conversionBuffer := ByteArray new:(bytesPerRowIn * rowsPerStrip).
    ].    
    
    offset := 1.
    stripNr := 1.
    row := 1.
    msb := (byteOrder == #msb).

    [row <= height] whileTrue:[
        self positionToStrip:stripNr.
        nBytes := stripByteCounts at:stripNr.
        rowsInThisStrip := stripRowCounts notNil 
                                ifTrue:[stripRowCounts at:stripNr]
                                ifFalse:[rowsPerStrip].
        bytesInThisStrip := bytesPerRowIn * rowsInThisStrip.
        
        "/ don't assert; the last strip is actually shorter in many images                       
        "/ compression == 1 ifTrue:[
        "/    self assert:( nBytes == (bytesPerRowIn * rowsInThisStrip) ).
        "/ ].
        
        conversionBuffer notNil ifTrue:[
            decompressorBlock notNil ifTrue:[
                inBuffer := ByteArray new:nBytes.
                (inStream nextBytes:nBytes into:inBuffer startingAt:1) == nBytes ifFalse:[
                    self fileFormatError:'short read'
                ]. 

                nDecompressed := decompressorBlock 
                                    value:inBuffer value:nBytes 
                                    value:conversionBuffer value:1
                                    value:bytesInThisStrip.
            ] ifFalse:[
                (nDecompressed := inStream nextBytes:nBytes into:conversionBuffer startingAt:1) == nBytes ifFalse:[
                    self fileFormatError:'short read'
                ]. 
            ].    
            convertFloats ifTrue:[
                self assert:(nDecompressed \\ 4) == 0.
                floats := FloatArray new:(nDecompressed // 4).
            ] ifFalse:[
                convertDoubles ifTrue:[
                    self assert:(nDecompressed \\ 8) == 0.
                    floats := DoubleArray new:(nDecompressed // 8).
                ] ifFalse:[
                    convertHalfFloats ifTrue:[  
                        self assert:(nDecompressed \\ 2) == 0.
                        floats := HalfFloatArray new:(nDecompressed // 2).
                    ].
                ].
            ].
            floats replaceBytesWith:conversionBuffer.
            1 to:floats size do:[:i |
                |dVal byteVal|

                dVal := floats at:i.
                "/ rescale from 0..1 to 0..255
                byteVal := (dVal * 255) asInteger clampBetween:0 and:255.
                data at:offset+i-1 put:byteVal.
            ].
        ] ifFalse:[
            decompressorBlock notNil ifTrue:[
                inBuffer := ByteArray new:nBytes.
                (inStream nextBytes:nBytes into:inBuffer startingAt:1) == nBytes ifFalse:[
                    self fileFormatError:'short read'
                ]. 
                nDecompressed := decompressorBlock 
                                    value:inBuffer value:nBytes 
                                    value:data value:offset 
                                    value:bytesInThisStrip.
            ] ifFalse:[    
                (inStream nextBytes:nBytes into:data startingAt:offset) == nBytes ifFalse:[
                    self fileFormatError:'short read'
                ]. 
            ]. 
        ].
        
        offset := offset + bytesInThisStrip.
        row := row + rowsInThisStrip.
        stripNr := stripNr + 1.
    ].

    swapInt16s ifTrue:[
        data swapBytes
    ].
    convertFillOrder ifTrue:[
        1 to:data size do:[:i |
            data at:i put:(data at:i) bitReversed8.
        ].    
    ].
    
    (predictor ~~ 1) ifTrue:[
        (predictor == 2) ifTrue:[
            self class decodeDelta:nPlanes in:data width:width height:height
        ] ifFalse:[
            ^ self fileFormatError:'unsupported predictor'
        ].    
    ].

    "Created: / 29-08-2017 / 23:15:10 / cg"
!

readJBIGTiffImageData
    ^ self fileFormatError:'jbig compression not implemented'.

    "Modified: / 3.2.1998 / 18:05:04 / cg"
!

readJPEGTiffImageData
    |nBytes compressedData|
    
    stripByteCounts size == 1 ifTrue:[
        "/ single strip
        self positionToStrip:1.
        nBytes := stripByteCounts at:1.
        compressedData := ByteArray uninitializedNew:nBytes.
        (inStream nextBytes:nBytes into:compressedData) == nBytes ifFalse:[ self error:'short read' ].
        "/ pngOrJPGImage := JPEGReader fromStream:compressedData readStream.
        "/ self halt.
    ].
    stripRowCounts notNil ifTrue:[
        ^ self fileFormatError:'stripRowCounts not supported'.
    ].
    
    ^ self fileFormatError:'jpeg (old) compression not implemented'.

    "Modified: / 27-08-2017 / 15:38:23 / cg"
!

readLZWTiffImageData
    self readImageDataUsingDecompressor:
        [:inBytes :inCount :outBytes :outOffset :outCount |
            self class 
                decompressLZWFrom:inBytes count:inCount
                into:outBytes startingAt:outOffset.
            outCount.
        ].

"/    "read LZW compressed tiff data; 
"/     this method only handles 8+8+8 and 8+8+8+8 rgb 
"/     and 2bit or 2+2bit greyscale images.
"/     For 2+2bit greyscale images, the alpha plane is ignored.
"/     (maybe other formats work also - but simply not tested)"
"/
"/    |bytesPerRow compressedStrip nPlanes overAllBytes
"/     bytesPerStrip "{ Class: SmallInteger }"
"/     nBytes        "{ Class: SmallInteger }"
"/     prevSize      "{ Class: SmallInteger }"
"/     stripNr       "{ Class: SmallInteger }"
"/     offset        "{ Class: SmallInteger }"
"/     row           "{ Class: SmallInteger }" |
"/
"/    nPlanes := samplesPerPixel.
"/
"/    (nPlanes >= 3) ifTrue:[
"/        (bitsPerSample conform:[:each | each == 8]) ifFalse:[
"/            ^ self fileFormatError:'only 8/8/8(/8) bits/sample are supported'.
"/        ].
"/        bytesPerRow := width * samplesPerPixel.
"/    ] ifFalse:[
"/        (nPlanes == 2) ifTrue:[
"/            (planarConfiguration ~~ PLANARCONFIG_SEPARATE) ifTrue:[
"/                ^ self fileFormatError:'only separate planes are supported'.
"/            ].
"/            'TIFFReader [info]: ignoring alpha plane' infoPrintCR.
"/            nPlanes := 1
"/        ].
"/        (nPlanes == 1) ifFalse:[
"/            ^ self fileFormatError:'unsupported nPlanes: ' , nPlanes printString, '; only 3-sample rgb / monochrome supported'.
"/        ].
"/        bytesPerRow := (width * (bitsPerSample at:1) + 7) // 8.
"/    ].
"/    stripRowCounts notNil ifTrue:[
"/        ^ self fileFormatError:'stripRowCounts not supported'.
"/    ].
"/
"/    "/ 'TIFFReader: decompressing LZW ...' infoPrintNL.
"/
"/    overAllBytes := bytesPerRow * height.
"/    bytesPerRow == width ifTrue:[
"/        data := ByteArray uninitializedNew:overAllBytes.
"/    ] ifFalse:[
"/        data := ByteArray new:overAllBytes.
"/    ].
"/
"/    offset := 1.
"/    stripNr := 0.
"/
"/    row := 1.
"/    bytesPerStrip := bytesPerRow * rowsPerStrip.
"/    prevSize := 0.
"/    [row <= height] whileTrue:[
"/        stripNr := stripNr + 1.
"/        self positionToStrip:stripNr.
"/        nBytes := stripByteCounts at:stripNr.
"/        (nBytes > prevSize) ifTrue:[
"/            compressedStrip := ByteArray uninitializedNew:nBytes.
"/            prevSize := nBytes
"/        ].
"/        (inStream nextBytes:nBytes into:compressedStrip) == nBytes ifFalse:[ self fileFormatError:'short file' ].
"/        self class 
"/            decompressLZWFrom:compressedStrip count:nBytes
"/            into:data startingAt:offset.
"/        offset := offset + bytesPerStrip.
"/        row := row + rowsPerStrip
"/    ].
"/
"/    predictor ~~ 1 ifTrue:[
"/        (predictor == 2) ifTrue:[
"/            self class decodeDelta:nPlanes in:data width:width height:height
"/        ] ifFalse:[
"/            self fileFormatError:'unsupported predictor'
"/        ]
"/    ]

    "Modified: / 29-08-2017 / 23:14:59 / cg"
!

readNeXTJPEGTiffImageData
    ^ self fileFormatError:'next jpeg compression not implemented'.

    "Modified: / 3.2.1998 / 18:10:45 / cg"
!

readNeXTRLE2TiffImageData
    ^ self fileFormatError:'next 2bit rle compression not implemented'.

    "Modified: / 3.2.1998 / 18:10:54 / cg"
!

readNewJPEGTiffImageData
    ^ self fileFormatError:'new jpeg compression not implemented'.
!

readPackbitsTiffImageData
    self readImageDataUsingDecompressor:
        [:inBytes :inCount :outBytes :outOffset :outCount |
            self class decompressTiffPackBitsFrom:inBytes to:outBytes at:outOffset count:outCount.
        ].

"/    "this has only been tested with monochrome images"
"/
"/    |bytesPerRow bitsPerRow nPlanes 
"/     stripNr       "{ Class: SmallInteger }"
"/     offset        "{ Class: SmallInteger }"
"/     row           "{ Class: SmallInteger }" 
"/     nBytes        "{ Class: SmallInteger }" bitsPerPixel overAllBytes buffer|
"/
"/    nPlanes := samplesPerPixel.
"/
"/    "only support 1-sample/pixel,
"/     with alpha - if separate planes,
"/     or rgb - if non separate planes and no alpha"
"/
"/    (nPlanes == 2) ifTrue:[
"/        (planarConfiguration ~~ PLANARCONFIG_SEPARATE) ifTrue:[
"/            ^ self fileFormatError:'with alpha, only separate planes supported'.
"/        ].
"/        'TIFFReader [info]: ignoring alpha plane' infoPrintCR.
"/        nPlanes := 1.
"/        bitsPerPixel := bitsPerSample at:1.
"/        bitsPerSample := Array with:bitsPerPixel.
"/        samplesPerPixel := 1.
"/    ] ifFalse:[
"/        (nPlanes == 3) ifTrue:[
"/            (planarConfiguration ~~ PLANARCONFIG_CONTIG) ifTrue:[
"/                ^ self fileFormatError:'only non separate planes supported'.
"/            ].
"/            bitsPerSample ~= #(8 8 8) ifTrue:[
"/                ^ self fileFormatError:'only 8/8/8 rgb images supported'.
"/            ].
"/            bitsPerPixel := 24
"/        ] ifFalse:[
"/            (nPlanes ~~ 1) ifTrue:[
"/                ^ self fileFormatError:('format (nplanes == %1) not supported' bindWith:nPlanes).
"/            ].
"/            bitsPerPixel := bitsPerSample at:1.
"/        ]
"/    ].
"/    stripRowCounts notNil ifTrue:[
"/        ^ self fileFormatError:'stripRowCounts not supported'.
"/    ].
"/
"/    bitsPerRow := width * bitsPerPixel.
"/    bytesPerRow := bitsPerRow // 8.
"/    ((bitsPerRow \\ 8) ~~ 0) ifTrue:[
"/        bytesPerRow := bytesPerRow + 1
"/    ].
"/
"/    overAllBytes := bytesPerRow * height.
"/    bytesPerRow == width ifTrue:[
"/        data := ByteArray uninitializedNew:overAllBytes.
"/    ] ifFalse:[
"/        data := ByteArray new:overAllBytes.
"/    ].
"/
"/    offset := 1.
"/    stripNr := 1.
"/
"/    buffer := nil.
"/    row := 1.
"/    [row <= height] whileTrue:[
"/        nBytes := stripByteCounts at:stripNr.
"/        self positionToStrip:stripNr.
"/
"/        nBytes > buffer size ifTrue:[
"/            "/ realloc
"/            buffer := ByteArray uninitializedNew:nBytes.
"/        ].
"/        (inStream nextBytes:nBytes into:buffer) == nBytes ifFalse:[
"/            self fileFormatError:'short file read'
"/        ].    
"/        self class decompressTiffPackBitsFrom:buffer to:data at:offset count:(bytesPerRow * rowsPerStrip).
"/        "/ nDecompressedBytes := self class decompressPackBits:nBytes from:buffer to:data startingAt:offset.
"/
"/        offset := offset + (bytesPerRow * rowsPerStrip).
"/        row := row + rowsPerStrip.
"/        stripNr := stripNr + 1.
"/    ]

    "Modified: / 29-08-2017 / 23:15:03 / cg"
!

readPixarFilmTiffImageData
    ^ self fileFormatError:'pixar film compression not implemented'.

    "Modified: / 3.2.1998 / 18:11:45 / cg"
!

readPixarLogTiffImageData
    ^ self fileFormatError:'pixar log compression not implemented'.

    "Modified: / 3.2.1998 / 18:11:53 / cg"
!

readSGI24TiffImageData
    ^ self fileFormatError:'SGI 24-bit Log Luminance encoding not implemented' .

    "Created: / 25-08-2017 / 11:17:25 / cg"
!

readSGI32TiffImageData
    ^ self fileFormatError:'SGI 32-bit Log Luminance encoding not implemented' .

    "Created: / 25-08-2017 / 11:17:21 / cg"
!

readThunderScanTiffImageData
    |bytesPerRow compressedStrip nPlanes overAllBytes
     bytesPerStrip "{ Class: SmallInteger }"
     nBytes        "{ Class: SmallInteger }"
     prevSize      "{ Class: SmallInteger }"
     stripNr       "{ Class: SmallInteger }"
     offset        "{ Class: SmallInteger }"
     row           "{ Class: SmallInteger }"
     pixelIndex
     i even gen highNibble lastPixel d1 d2 d3|

    nPlanes := samplesPerPixel.

    (nPlanes == 2) ifTrue:[
        (planarConfiguration ~~ PLANARCONFIG_SEPARATE) ifTrue:[
            ^ self fileFormatError:'only separate planes are supported'.
        ].
        'TIFFReader [info]: ignoring alpha plane' infoPrintCR.
        nPlanes := 1
    ].
    (nPlanes == 1) ifFalse:[
        ^ self fileFormatError:'unsupported nPlanes: ' , nPlanes printString.
    ].
    (bitsPerSample at:1) == 4 ifFalse:[
        ^ self fileFormatError:('unsupported bitsPerSample: %1 (only 4 supported)' bindWith:(bitsPerSample at:1)).
    ].    
    stripRowCounts notNil ifTrue:[
        ^ self fileFormatError:'stripRowCounts not supported'.
    ].

    bytesPerRow := (width * (bitsPerSample at:1) + 7) // 8.

    "/ 'TIFFReader: decompressing ThunderScan ...' infoPrintNL.

    overAllBytes := bytesPerRow * height.
    bytesPerRow == width ifTrue:[
        data := ByteArray uninitializedNew:overAllBytes.
    ] ifFalse:[
        data := ByteArray new:overAllBytes.
    ].

    offset := 1.
    stripNr := 0.
    
    gen := [:pixel | 
                even ifTrue:[
                    highNibble := pixel. 
                    even := false.
                ] ifFalse:[    
                    data at:pixelIndex put:((highNibble bitShift:4) bitOr:pixel).
                    pixelIndex := pixelIndex + 1.
                    even := true.
                ].
            ].    
    even := true.
    lastPixel := 0.
    
    row := 1.
    bytesPerStrip := bytesPerRow * rowsPerStrip.
    prevSize := 0.
    [row <= height] whileTrue:[
        stripNr := stripNr + 1.
        self positionToStrip:stripNr.
        nBytes := stripByteCounts at:stripNr.
        (nBytes > prevSize) ifTrue:[
            compressedStrip := ByteArray uninitializedNew:nBytes.
            prevSize := nBytes
        ].
        (inStream nextBytes:nBytes into:compressedStrip) == nBytes ifFalse:[ self error:'short read' ].

        "/ RLE decode... (see http://fileformats.archiveteam.org/wiki/ThunderScan_compression)
        i := 1.
        pixelIndex := offset.
        
        [i <= nBytes] whileTrue:[
            |code|

            code := compressedStrip at:i.
            i := i + 1.
            code >= 2r11000000 ifTrue:[
                "/ a single pixel
                lastPixel := code bitAnd:2r00111111.
                self assert:(lastPixel <= 2r1111).
                
                gen value:lastPixel.
            ] ifFalse:[
                code >= 2r10000000 ifTrue:[
                    "/ three bit deltas (2 pixels)

                    d1 := (code rightShift:3) bitAnd:2r111.
                    d2 := code bitAnd:2r111.
                    d1 ~~ 4 ifTrue:[
                        lastPixel := lastPixel + (#(0 1 2 3 0 -3 -2 -1) at:d1+1).
                        gen value:lastPixel.
                    ].
                    d2 ~~ 4 ifTrue:[
                        lastPixel := lastPixel + (#(0 1 2 3 0 -3 -2 -1) at:d2+1).
                        gen value:lastPixel.
                    ].
                ] ifFalse:[
                    code >= 2r01000000 ifTrue:[
                        "/ two bit deltas (3 pixels)    
                        d1 := (code rightShift:4) bitAnd:2r11.
                        d2 := (code rightShift:2) bitAnd:2r11.
                        d3 := code bitAnd:2r11.
                        d1 ~~ 2 ifTrue:[
                            lastPixel := lastPixel + (#(0 1 0 -1) at:d1+1).
                            gen value:lastPixel.
                        ].
                        d2 ~~ 2 ifTrue:[
                            lastPixel := lastPixel + (#(0 1 0 -1) at:d2+1).
                            gen value:lastPixel.
                        ].
                        d3 ~~ 2 ifTrue:[
                            lastPixel := lastPixel + (#(0 1 0 -1) at:d3+1).
                            gen value:lastPixel.
                        ].
                    ] ifFalse:[
                        code timesRepeat:[ gen value:lastPixel ].
                    ].
                ].
            ].
        ].
        "/ self assert:(pixelIndex == (offset + bytesPerStrip)).
        
        offset := offset + bytesPerStrip.
        row := row + rowsPerStrip
    ].

    (predictor ~~ 1) ifTrue:[
        ^ self fileFormatError:('unsupported predictor: %1' bindWith:predictor).
    ].

    "Modified: / 27-08-2017 / 15:38:47 / cg"
!

readTiffImageData
    (compression == COMPRESSION_NONE " 1 " ) ifTrue:[
        self readUncompressedTiffImageData.
        ^ self
    ].
    (compression == COMPRESSION_CCITTRLE " 2 ") ifTrue:[
        self readCCITT3RLETiffImageData.
        ^ self
    ].
    (compression == COMPRESSION_CCITTFAX3 " 3 ") ifTrue:[
        self readCCITTGroup3TiffImageData.
        ^ self
    ]. 
    (compression == COMPRESSION_CCITTFAX4 " 4 ") ifTrue:[
        self readCCITTGroup4TiffImageData.
        ^ self
    ]. 
    (compression == COMPRESSION_LZW " 5 ") ifTrue:[
        self readLZWTiffImageData.
        ^ self
    ].
    (compression == COMPRESSION_OJPEG " 6 ") ifTrue:[
        self readJPEGTiffImageData.
        ^ self
    ].
    (compression == COMPRESSION_JPEG " 7 ") ifTrue:[
        self readNewJPEGTiffImageData.
        ^ self
    ].
    (compression == COMPRESSION_ADOBE_DEFLATE " 8 ") ifTrue:[
        self readAdobeDeflateTiffImageData.
        ^ self
    ].

    (compression == COMPRESSION_NEXT " 32766 ") ifTrue:[
        self readNeXTRLE2TiffImageData.
        ^ self
    ].
    (compression == COMPRESSION_CCITTRLEW " 32771 ") ifTrue:[
        self readCCITTRLEWTiffImageData.
        ^ self
    ].
    (compression == COMPRESSION_PACKBITS " 32773 ") ifTrue:[
        self readPackbitsTiffImageData.
        ^ self
    ].
    (compression == COMPRESSION_THUNDERSCAN " 32809 ") ifTrue:[
        self readThunderScanTiffImageData.
        ^ self
    ].
    (compression == COMPRESSION_PIXARFILM " 32908 ") ifTrue:[
        self readPixarFilmTiffImageData.
        ^ self
    ].
    (compression == COMPRESSION_PIXARLOG " 32909 ") ifTrue:[
        self readPixarLogTiffImageData.
        ^ self
    ].
    (compression == COMPRESSION_DEFLATE " 32946 ") ifTrue:[
        self readDeflateTiffImageData.
        ^ self
    ].
    (compression == COMPRESSION_DCS " 32947 ") ifTrue:[
        self readDCSTiffImageData.
        ^ self
    ].
    (compression == COMPRESSION_NEXT_JPEG " 32865 ") ifTrue:[
        self readNeXTJPEGTiffImageData.
        ^ self
    ].
    (compression == COMPRESSION_JBIG " 34661 ") ifTrue:[
        self readJBIGTiffImageData.
        ^ self
    ].
    (compression == COMPRESSION_SGILOG " 34676 ") ifTrue:[
        self readSGI32TiffImageData.
        ^ self
    ].
    (compression == COMPRESSION_SGILOG24 " 34677 ") ifTrue:[
        self readSGI24TiffImageData.
        ^ self
    ].

    self fileFormatError:('compression type ' , compression printString , ' not known').

    "Created: / 11-04-1997 / 00:19:44 / cg"
    "Modified: / 26-08-2017 / 21:46:56 / cg"
!

readTiledJPEGTiffImageData
    ^ self fileFormatError:'tiled jpeg (old) compression not implemented'.

    "Created: / 25-08-2017 / 16:27:28 / cg"
    "Modified: / 26-08-2017 / 13:39:04 / cg"
!

readTiledLZWTiffImageData
    ^ self fileFormatError:'tiled LZW data not implemented' .

    "Created: / 25-08-2017 / 01:05:13 / cg"
!

readTiledNewJPEGTiffImageData
    ^ self fileFormatError:'tiled new jpeg compression not implemented'.

    "Created: / 26-08-2017 / 13:37:51 / cg"
!

readTiledTiffImageData
    (compression == COMPRESSION_NONE " 1 ") ifTrue:[
        self readTiledUncompressedTiffImageData.
        ^ self
    ].
    (compression == COMPRESSION_LZW " 5 ") ifTrue:[
        self readTiledLZWTiffImageData.
        ^ self
    ].
    (compression == COMPRESSION_OJPEG " 6 ") ifTrue:[
        self readTiledJPEGTiffImageData.
        ^ self
    ].
    (compression == COMPRESSION_JPEG " 7 ") ifTrue:[
        self readTiledNewJPEGTiffImageData.
        ^ self
    ].

    self fileFormatError:('tiled compression type ' , compression printString , ' not supported').

    "Created: / 25-08-2017 / 00:19:14 / cg"
    "Modified: / 26-08-2017 / 13:39:15 / cg"
!

readTiledUncompressedTiffImageData
    |bytesPerRow     "{ Class: SmallInteger }"
     bitsPerRow      "{ Class: SmallInteger }"
     bytesPerTileRow "{ Class: SmallInteger }"
     bitsPerTileRow  "{ Class: SmallInteger }" 
     nPlanes 
     tileNr          "{ Class: SmallInteger }"
     "/ offset          "{ Class: SmallInteger }"
     "/ row             "{ Class: SmallInteger }" 
     nBytes          "{ Class: SmallInteger }"
     bitsPerPixel    "{ Class: SmallInteger }"
     overAllBytes    "{ Class: SmallInteger }"
     where           "{ Class: SmallInteger }"
     x               "{ Class: SmallInteger }"       
     y               "{ Class: SmallInteger }" 
     imageRowOffset  "{ Class: SmallInteger }" 
     imageOffset     "{ Class: SmallInteger }" 
     tileOffset      "{ Class: SmallInteger }"   
     dataOffset      "{ Class: SmallInteger }"   
     tilePos tile tH tW tb|

    nPlanes := samplesPerPixel.

    "/ not all formats are supported here,

    (nPlanes == 2) ifTrue:[
        (planarConfiguration ~~ PLANARCONFIG_SEPARATE) ifTrue:[
            ^ self fileFormatError:'with alpha, only separate planes supported'.
        ].
        'TIFFReader [info]: ignoring alpha plane' infoPrintCR.
        nPlanes := 1.
        bitsPerPixel := bitsPerSample at:1.
        bitsPerSample := Array with:bitsPerPixel.
        samplesPerPixel := 1.
    ] ifFalse:[
        (nPlanes == 4) ifTrue:[
            (planarConfiguration ~~ PLANARCONFIG_CONTIG) ifTrue:[
                ^ self fileFormatError:'only non separate planes supported'.
            ].
            bitsPerSample ~= #(8 8 8 8) ifTrue:[
                ^ self fileFormatError:'only 8/8/8/8 images supported (is: ' , bitsPerSample printString , ')'.
            ].
            bitsPerPixel := 32.
        ] ifFalse:[
            (nPlanes == 3) ifTrue:[
                (planarConfiguration ~~ PLANARCONFIG_CONTIG) ifTrue:[
                    ^ self fileFormatError:'only non separate planes supported'.
                ].
                bitsPerSample ~= #(8 8 8) ifTrue:[
                    ^ self fileFormatError:'only 8/8/8 images supported (is: ' , bitsPerSample printString , ')'.
                ].
                bitsPerPixel := 24
            ] ifFalse:[
                (nPlanes ~~ 1) ifTrue:[
                    ^ self fileFormatError:('unsupported format: nplanes=' , nPlanes printString).
                ].
                bitsPerPixel := bitsPerSample at:1.
            ]
        ]
    ].
    stripRowCounts notNil ifTrue:[
        ^ self fileFormatError:'stripRowCounts not supported'.
    ].

    bitsPerRow := width * bitsPerPixel.
    bytesPerRow := (bitsPerRow + 7) // 8.
    
    bitsPerTileRow := tileWidth * bitsPerPixel.
    bytesPerTileRow := (bitsPerTileRow + 7) // 8.

    overAllBytes := bytesPerRow * height.
    data := ByteArray new:overAllBytes.
    
    tileNr := 1.
    where := -1.
    y := 0.
    imageRowOffset := 1.
    [ y < height ] whileTrue:[
        tH := tileLength.
        (y+tileLength) > height ifTrue:[
            tH := height - y.
        ].    
        x := 0.
        imageOffset := imageRowOffset.
        [ x < width ] whileTrue:[
            nBytes := tileByteCounts at:tileNr.
            tilePos := tileOffsets at:tileNr.
            tileNr := tileNr + 1.
            
            where ~~ tilePos ifTrue:[
                inStream position:tilePos.
                where := tilePos.
            ].
            tile := ByteArray new:nBytes.
            inStream nextBytes:nBytes into:tile startingAt:1 blockSize:4096.

            "/ copy the tile.
            tileOffset := 1.
            dataOffset := imageOffset. 
            tW := tileWidth.
            tb := bytesPerTileRow.
            (x+tileWidth) > width ifTrue:[
                tW := width-x.
                tb := ((bitsPerPixel * tW) + 7) // 8.
            ].    
            1 to:tH do:[:yT |
                data replaceFrom:dataOffset to:dataOffset+tb-1 with:tile startingAt:tileOffset.
                dataOffset := dataOffset + bytesPerRow.
                tileOffset := tileOffset + bytesPerTileRow.
            ].
            
            "/ offset := offset + nBytes.
            "/ row := row + rowsPerStrip.
            where := where + nBytes.

            x := x + tW.
            imageOffset := imageOffset + bytesPerTileRow.
        ].
        y := y + tH.
        imageRowOffset := imageRowOffset + (bytesPerRow*tH).
    ].

    "Created: / 25-08-2017 / 00:22:31 / cg"
    "Modified: / 28-08-2017 / 01:14:00 / cg"
!

readUncompressedTiffImageData
    self readImageDataUsingDecompressor:nil
    
"/    |bytesPerRow   "{ Class: SmallInteger }"
"/     bitsPerRow    "{ Class: SmallInteger }"
"/     nPlanes 
"/     stripNr       "{ Class: SmallInteger }"
"/     offset        "{ Class: SmallInteger }"
"/     row           "{ Class: SmallInteger }" 
"/     nBytes        "{ Class: SmallInteger }"
"/     bitsPerPixel 
"/     overAllBytes  "{ Class: SmallInteger }"
"/     where         "{ Class: SmallInteger }"
"/     stripPos 
"/     convertFloats convertDoubles convertHalfFloats|
"/
"/    nPlanes := samplesPerPixel.
"/
"/    "/ not all formats are supported here,
"/
"/    (nPlanes == 2) ifTrue:[
"/        (planarConfiguration ~~ PLANARCONFIG_SEPARATE) ifTrue:[
"/            ^ self fileFormatError:'with alpha, only separate planes supported'.
"/        ].
"/        'TIFFReader [info]: ignoring alpha plane' infoPrintCR.
"/        nPlanes := 1.
"/        bitsPerPixel := bitsPerSample at:1.
"/        bitsPerSample := Array with:bitsPerPixel.
"/        samplesPerPixel := 1.
"/    ] ifFalse:[
"/        (nPlanes == 4) ifTrue:[
"/            (planarConfiguration ~~ PLANARCONFIG_CONTIG) ifTrue:[
"/                ^ self fileFormatError:'only non separate planes supported'.
"/            ].
"/            bitsPerSample ~= #(8 8 8 8) ifTrue:[
"/                bitsPerSample ~= #(16 16 16 16) ifTrue:[
"/                    ^ self fileFormatError:'only 8/8/8/8 and 16/16/16/16 images supported'.
"/                ].
"/            ].
"/            bitsPerPixel := bitsPerSample sum.
"/        ] ifFalse:[
"/            (nPlanes == 3) ifTrue:[
"/                (planarConfiguration ~~ 1) ifTrue:[
"/                    ^ self fileFormatError:'only non separate planes supported'.
"/                ].
"/                bitsPerSample ~= #(8 8 8) ifTrue:[
"/                    bitsPerSample ~= #(16 16 16) ifTrue:[
"/                        ^ self fileFormatError:'only 8/8/8 and 16/16/16 images supported; is: ' , bitsPerSample printString.
"/                    ].
"/                ].
"/                bitsPerPixel := bitsPerSample sum.
"/            ] ifFalse:[
"/                (nPlanes ~~ 1) ifTrue:[
"/                    ^ self fileFormatError:('unsupported format: nplanes=' , nPlanes printString).
"/                ].
"/                bitsPerPixel := bitsPerSample at:1.
"/            ]
"/        ]
"/    ].
"/    stripRowCounts notNil ifTrue:[
"/        ^ self fileFormatError:'stripRowCounts not supported'.
"/    ].
"/
"/    bitsPerRow := width * bitsPerPixel.
"/    bytesPerRow := bitsPerRow // 8.
"/    ((bitsPerRow \\ 8) ~~ 0) ifTrue:[
"/        bytesPerRow := bytesPerRow + 1
"/    ].
"/
"/    overAllBytes := bytesPerRow * height.
"/    bytesPerRow == width ifTrue:[
"/        data := ByteArray uninitializedNew:overAllBytes.
"/    ] ifFalse:[
"/        data := ByteArray new:overAllBytes.
"/    ].
"/
"/    offset := 0.
"/    stripNr := 0.
"/    where := -1.
"/    row := 1.
"/    [row <= height] whileTrue:[
"/        stripNr := stripNr + 1.
"/        nBytes := stripByteCounts at:stripNr.
"/        stripPos := stripOffsets at:stripNr.
"/        where ~~ stripPos ifTrue:[
"/            inStream position:stripPos.
"/            where := stripPos.
"/        ].
"/        
"/        offset + nBytes > overAllBytes ifTrue:[
"/            nBytes := overAllBytes - offset.
"/        ].
"/
"/        (inStream nextBytes:nBytes into:data startingAt:offset+1) == nBytes ifFalse:[
"/            ^ self fileFormatError:'short read'.
"/        ].    
"/
"/        offset := offset + nBytes.
"/        row := row + rowsPerStrip.
"/        where := where + nBytes.
"/    ].

    "Modified: / 29-08-2017 / 23:15:07 / cg"
! !

!TIFFReader methodsFor:'private-reading'!

decodePhotoshopImageResourceBlock:bytes
    "8BIM is the signature for Photoshop Image Resource Block (IRB). 
     See http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_38034.
     
     This kind of information could be found in images such as TIFF, JPEG, Photoshop native image format etc. 
     It could also be found in non-image documents such as in PDF.
     The structure of the IRB is as follows:
     Each IRB block starts with 4 bytes signature which translates to string '8BIM.' 
     After that, is a 2 bytes unique identifier denoting the kind of resource for this IRB. 
     For example: 
        0x040c for thumbnail; 
        0x041a for slices; 
        0x0408 for grid information; 
        0x040f for ICC Profile etc.

     After the identifier is a variable length string for name. 
     The first byte of the string tells the length of the string (excluding the first length byte). 
     After the first byte comes the string itself. 
     There is a requirement that the length of the whole string (including the length byte) should be even. 
     Otherwise, pad one more byte after the string.

     The next 4 bytes specifies the size of the actual data for this resource block followed by the data with the specified length. 
     The total length of the data also should be an even number. So if the size of the data is odd, pad another one byte. 
     This finishes a whole 8BIM.

     There could be more than one IRBs but they all conform to the same structure as described above. 
     How to interpret the data depends on the unique identifier.

     Now let's see how the IRBs are include in images. 
     For a JPEG image, metadata could be present as one of the application (APPn) segment. 
     Since different application could use the same APPn segment to store it's own metadata, 
     there must be some kind of identifier to let the image reader know what kind of information is contained inside the APPn. 
     Photoshop uses APP13 as it's IRB container and the APP13 contains 'Photoshop 3.0' as it's identifier.

     For TIFF image which is tag based and arranged in a directory structure. 
     There is a private tag 16r8649 called 'PHOTOSHOP' to insert IRB information.

    0x03E8 (Obsolete--Photoshop 2.0 only ) Contains five 2-byte values: number of channels, rows, columns, depth, and mode
    0x03E9 Macintosh print manager print info record
    0x03EA Macintosh page format information. No longer read by Photoshop. (Obsolete)
    0x03EB Obsolete--Photoshop 2.0 only ) Indexed color table
    0x03ED ResolutionInfo structure. See Appendix A in Photoshop API Guide.pdf.
    0x03EE Names of the alpha channels as a series of Pascal strings.
    0x03EF (Obsolete) See ID 1077DisplayInfo structure. See Appendix A in Photoshop API Guide.pdf.
    0x03F0 The caption as a Pascal string.
    0x03F1 Border information. Contains a fixed number (2 bytes real, 2 bytes fraction) for the border width, and 2 bytes for border units (1 = inches, 2 = cm, 3 = points, 4 = picas, 5 = columns).
    0x03F2 Background color. See See Color structure.
    0x03F3 Print flags. A series of one-byte boolean values (see Page Setup dialog): labels, crop marks, color bars, registration marks, negative, flip, interpolate, caption, print flags.
    0x03F4 Grayscale and multichannel halftoning information
    0x03F5 Color halftoning information
    0x03F6 Duotone halftoning information
    0x03F7 Grayscale and multichannel transfer function
    0x03F8 Color transfer functions
    0x03F9 Duotone transfer functions
    0x03FA Duotone image information
    0x03FB Two bytes for the effective black and white values for the dot range
    0x03FC (Obsolete)
    0x03FD EPS options
    0x03FE Quick Mask information. 2 bytes containing Quick Mask channel ID; 1- byte boolean indicating whether the mask was initially empty.
    0x03FF (Obsolete)
    0x0400 Layer state information. 2 bytes containing the index of target layer (0 = bottom layer).
    0x0401 Working path (not saved). See See Path resource format.
    0x0402 Layers group information. 2 bytes per layer containing a group ID for the dragging groups. Layers in a group have the same group ID.
    0x0403 (Obsolete)
    0x0404 IPTC-NAA record. Contains the File Info... information. See the documentation in the IPTC folder of the Documentation folder.
    0x0405 Image mode for raw format files
    0x0406 JPEG quality. Private.
    0x0408 (Photoshop 4.0) Grid and guides information. See See Grid and guides resource format.
    0x0409 (Photoshop 4.0) Thumbnail resource for Photoshop 4.0 only. See See Thumbnail resource format.
    0x040A (Photoshop 4.0) Copyright flag. Boolean indicating whether image is copyrighted. Can be set via Property suite or by user in File Info...
    0x040B (Photoshop 4.0) URL. Handle of a text string with uniform resource locator. Can be set via Property suite or by user in File Info...
    0x040C (Photoshop 5.0) Thumbnail resource (supersedes resource 1033). See See Thumbnail resource format.
    0x040D (Photoshop 5.0) Global Angle. 4 bytes that contain an integer between 0 and 359, which is the global lighting angle for effects layer. If not present, assumed to be 30.
    0x040E (Obsolete) See ID 1073 below. (Photoshop 5.0) Color samplers resource. See See Color samplers resource format.
    0x040F (Photoshop 5.0) ICC Profile. The raw bytes of an ICC (International Color Consortium) format profile. See ICC1v42_2006-05.pdf in the Documentation folder and icProfileHeader.h in Sample Code\Common\Includes .
    0x0410 (Photoshop 5.0) Watermark. One byte.
    0x0411 (Photoshop 5.0) ICC Untagged Profile. 1 byte that disables any assumed profile handling when opening the file. 1 = intentionally untagged.
    0x0412 (Photoshop 5.0) Effects visible. 1-byte global flag to show/hide all the effects layer. Only present when they are hidden.
    0x0413 (Photoshop 5.0) Spot Halftone. 4 bytes for version, 4 bytes for length, and the variable length data.
    0x0414 (Photoshop 5.0) Document-specific IDs seed number. 4 bytes: Base value, starting at which layer IDs will be generated (or a greater value if existing IDs already exceed it). Its purpose is to avoid the case where we add layers, flatten, save, open, and then add more layers that end up with the same IDs as the first set.
    0x0415 (Photoshop 5.0) Unicode Alpha Names. Unicode string
    0x0416 (Photoshop 6.0) Indexed Color Table Count. 2 bytes for the number of colors in table that are actually defined
    0x0417 (Photoshop 6.0) Transparency Index. 2 bytes for the index of transparent color, if any.
    0x0419 (Photoshop 6.0) Global Altitude. 4 byte entry for altitude
    0x041A (Photoshop 6.0) Slices. See See Slices resource format.
    0x041B (Photoshop 6.0) Workflow URL. Unicode string
    0x041C (Photoshop 6.0) Jump To XPEP. 2 bytes major version, 2 bytes minor version, 4 bytes count. Following is repeated for count: 4 bytes block size, 4 bytes key, if key = 'jtDd' , then next is a Boolean for the dirty flag; otherwise it's a 4 byte entry for the mod date.
    0x041D (Photoshop 6.0) Alpha Identifiers. 4 bytes of length, followed by 4 bytes each for every alpha identifier.
    0x041E (Photoshop 6.0) URL List. 4 byte count of URLs, followed by 4 byte long, 4 byte ID, and Unicode string for each count.
    0x0421 (Photoshop 6.0) Version Info. 4 bytes version, 1 byte hasRealMergedData , Unicode string: writer name, Unicode string: reader name, 4 bytes file version.
    0x0422 (Photoshop 7.0) EXIF data 1. See http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf
    0x0423 (Photoshop 7.0) EXIF data 3. See http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf
    0x0424 (Photoshop 7.0) XMP metadata. File info as XML description. See http://www.adobe.com/devnet/xmp/
    0x0425 (Photoshop 7.0) Caption digest. 16 bytes: RSA Data Security, MD5 message-digest algorithm
    0x0426 (Photoshop 7.0) Print scale. 2 bytes style (0 = centered, 1 = size to fit, 2 = user defined). 4 bytes x location (floating point). 4 bytes y location (floating point). 4 bytes scale (floating point)
    0x0428 (Photoshop CS) Pixel Aspect Ratio. 4 bytes (version = 1 or 2), 8 bytes double, x / y of a pixel. Version 2, attempting to correct values for NTSC and PAL, previously off by a factor of approx. 5%.
    0x0429 (Photoshop CS) Layer Comps. 4 bytes (descriptor version = 16), Descriptor (see See Descriptor structure)
    0x042A (Photoshop CS) Alternate Duotone Colors. 2 bytes (version = 1), 2 bytes count, following is repeated for each count: [ Color: 2 bytes for space followed by 4 * 2 byte color component ], following this is another 2 byte count, usually 256, followed by Lab colors one byte each for L, a, b. This resource is not read or used by Photoshop.
    0x042B (Photoshop CS)Alternate Spot Colors. 2 bytes (version = 1), 2 bytes channel count, following is repeated for each count: 4 bytes channel ID, Color: 2 bytes for space followed by 4 * 2 byte color component. This resource is not read or used by Photoshop.
    0x042D (Photoshop CS2) Layer Selection ID(s). 2 bytes count, following is repeated for each count: 4 bytes layer ID
    0x042E (Photoshop CS2) HDR Toning information
    0x042F (Photoshop CS2) Print info
    0x0430 (Photoshop CS2) Layer Group(s) Enabled ID. 1 byte for each layer in the document, repeated by length of the resource. NOTE: Layer groups have start and end markers
    0x0431 (Photoshop CS3) Color samplers resource. Also see ID 1038 for old format. See See Color samplers resource format.
    0x0432 (Photoshop CS3) Measurement Scale. 4 bytes (descriptor version = 16), Descriptor (see See Descriptor structure)
    0x0433 (Photoshop CS3) Timeline Information. 4 bytes (descriptor version = 16), Descriptor (see See Descriptor structure)
    0x0434 (Photoshop CS3) Sheet Disclosure. 4 bytes (descriptor version = 16), Descriptor (see See Descriptor structure)
    0x0435 (Photoshop CS3) DisplayInfo structure to support floating point clors. Also see ID 1007. See Appendix A in Photoshop API Guide.pdf .
    0x0436 (Photoshop CS3) Onion Skins. 4 bytes (descriptor version = 16), Descriptor (see See Descriptor structure)
    0x0438 (Photoshop CS4) Count Information. 4 bytes (descriptor version = 16), Descriptor (see See Descriptor structure) Information about the count in the document. See the Count Tool.
    0x043A (Photoshop CS5) Print Information. 4 bytes (descriptor version = 16), Descriptor (see See Descriptor structure) Information about the current print settings in the document. The color management options.
    0x043B (Photoshop CS5) Print Style. 4 bytes (descriptor version = 16), Descriptor (see See Descriptor structure) Information about the current print style in the document. The printing marks, labels, ornaments, etc.
    0x043C (Photoshop CS5) Macintosh NSPrintInfo. Variable OS specific info for Macintosh. NSPrintInfo. It is recommened that you do not interpret or use this data.
    0x043D (Photoshop CS5) Windows DEVMODE. Variable OS specific info for Windows. DEVMODE. It is recommened that you do not interpret or use this data.
    0x043E (Photoshop CS6) Auto Save File Path. Unicode string. It is recommened that you do not interpret or use this data.
    0x043F (Photoshop CS6) Auto Save Format. Unicode string. It is recommened that you do not interpret or use this data.
    0x0440 (Photoshop CC) Path Selection State. 4 bytes (descriptor version = 16), Descriptor (see See Descriptor structure) Information about the current path selection state.
    0x07D0-0x0BB6 Path Information (saved paths). See See Path resource format.
    0x0BB7 Name of clipping path. See See Path resource format.
    0x0BB8 (Photoshop CC) Origin Path Info. 4 bytes (descriptor version = 16), Descriptor (see See Descriptor structure) Information about the origin path data.
    0x0FA0-0x1387 Plug-In resource(s). Resources added by a plug-in. See the plug-in API found in the SDK documentation
    0x1B58 Image Ready variables. XML representation of variables definition
    0x1B59 Image Ready data sets
    0x1B5A Image Ready default selected state
    0x1B5B Image Ready 7 rollover expanded state
    0x1B5C Image Ready rollover expanded state
    0x1B5D Image Ready save layer settings
    0x1B5E Image Ready version
    0x1F40 (Photoshop CS3) Lightroom workflow, if present the document is in the middle of a Lightroom workflow.
    0x2710 Print flags information. 2 bytes version ( = 1), 1 byte center crop marks, 1 byte ( = 0), 4 bytes bleed width value, 2 bytes bleed width scale.
    "

    |s id name len resource record|

    record := OrderedCollection new.
    
    s := bytes readStream.
    [s atEnd] whileFalse:[
        (s next:4) asString = '8BIM' ifTrue:[
            id := s nextUnsignedInt16MSB.
            name := s upTo:0.
            name size odd ifFalse:[ s next ]. "/ padding
            len := s nextUnsignedInt32MSB.
            resource := s next:len.
            Verbose == true ifTrue:[
                Transcript showCR:('      8BIM-%1: %2' bindWith:(id hexPrintString:4) with:resource).
            ].
            record add:(Dictionary new
                            at:#id put:id;
                            at:#name put:name;
                            at:#resource put:resource;
                            yourself).                    
        ].    
    ].
    metaData at:#'PhotoshopIRB' put:record.

    "Created: / 27-08-2017 / 17:12:25 / cg"
    "Modified: / 27-08-2017 / 18:14:56 / cg"
!

decodeTiffTag:tagType numberType:numberType length:length
    |offset value valueArray 
     val scaleFactor rV gV bV
     n  "{ Class: SmallInteger }"
     i2 "{ Class: SmallInteger }"
     i3 "{ Class: SmallInteger }" |

    Verbose == true ifTrue:[ Logger info:'tiffTag: %1' with:tagType ].

    (numberType == 3 "TIFF_SHORT") ifTrue:[
        "16 bit ushort"
        valueArray := self readShorts:length signed:false.
        value := valueArray at:1
    ] ifFalse:[(numberType == 4 "TIFF_LONG") ifTrue:[
        "32 bit uinteger"
        valueArray := self readLongs:length signed:false.
        value := valueArray at:1
    ] ifFalse:[(numberType == 2 "TIFF_ASCII") ifTrue:[
        "ascii characters"
        value := self readChars:length
    ] ifFalse:[(numberType == 5 "TIFF_RATIONAL") ifTrue:[
        "64 (32+32) bit ufraction"
        valueArray := self readFracts:length signed:false.
        value := valueArray at:1
    ] ifFalse:[(numberType == 1 "TIFF_BYTE") ifTrue:[
        "8bit uinteger"
        value := self readBytes:length signed:false
    ] ifFalse:[(numberType == 6 "TIFF_SBYTE") ifTrue:[
        "TIFF6: 8bit signed integer"
        value := self readBytes:length  signed:true
    ] ifFalse:[(numberType == 8 "TIFF_SSHORT") ifTrue:[
        "TIFF6: 16bit signed integer"
        valueArray := self readShorts:length signed:true.
        value := valueArray at:1
    ] ifFalse:[(numberType == 9 "TIFF_SLONG") ifTrue:[
        "TIFF6: 32bit signed integer"
        valueArray := self readLongs:length signed:true.
        value := valueArray at:1
    ] ifFalse:[(numberType == 10 "TIFF_SRATIONAL") ifTrue:[
        "TIFF6: 64 (32+32) bit signed fraction"
        valueArray := self readFracts:length signed:true.
        value := valueArray at:1
    ] ifFalse:[(numberType == 11 "TIFF_FLOAT") ifTrue:[
        "TIFF6: 32 bit IEEE float"
        valueArray := self readFloats:length.
        value := valueArray at:1
    ] ifFalse:[(numberType == 12 "TIFF_DOUBLE") ifTrue:[
        "TIFF6: 64 bit IEEE double"
        valueArray := self readDoubles:length.
        value := valueArray at:1
        
    ] ifFalse:[(numberType == 7 "TIFF_UNDEFINED") ifTrue:[
        "8bit anything"
        value := self readBytes:length signed:false

    "/ the following are preps for the propsed bigTiff format    
    ] ifFalse:[(numberType == 16 "TIFF_LONG8") ifTrue:[
        "BIGTIFF: 8-byte unsigned integer"
        valueArray := self readLong8s:length signed:false.
        value := valueArray at:1.
    ] ifFalse:[(numberType == 17 "TIFF_SLONG8") ifTrue:[
        "BIGTIFF: 8-byte signed integer"
        valueArray := self readLong8s:length signed:true.
        value := valueArray at:1.
    ] ifFalse:[(numberType == 18 "TIFF_IFD8") ifTrue:[
        "BIGTIFF: 8-byte unsigned IFD offset"
        valueArray := self readLong8s:length signed:false.
        value := valueArray at:1.
    ] ifFalse:[
        isBigTiff ifTrue:[
            offset := (inStream nextInt64MSB:(byteOrder ~~ #lsb))
        ] ifFalse:[    
            offset := (inStream nextInt32MSB:(byteOrder ~~ #lsb))
        ]
    ]]]]]]]]]]]]]]].

    (tagType between:200 and:299) ifTrue:[
        (tagType == 254) ifTrue:[
            "/ New SubfileType
            "/      REDUCEDIMAGE    -> 1
            "/      PAGE            -> 2
            "/      MASK            -> 4
            "newSubFileType := value."

            "/ 'newSubfiletype ' print. value printNewline.
            Verbose == true ifTrue:[ 
                Logger info:'      newSubfiletype: %1' with:value
            ].
            ^ self
        ].
        (tagType == 255) ifTrue:[
            "/ Old SubfileType
            "/      IMAGE           -> 1
            "/      REDUCEDIMAGE    -> 2
            "/      PAGE            -> 3
            subFileType := value.

            Verbose == true ifTrue:[ 
                Logger info:'      oldSubfiletype: %1' with:value 
            ].

            ^ self
        ].
        (tagType == 256) ifTrue:[
            "ImageWidth"
            width := value.

            Verbose == true ifTrue:[ 
                Logger info:'      width: %1' with:value 
            ].

            ^ self
        ].
        (tagType == 257) ifTrue:[
            "ImageHeight"
            height := value.

            Verbose == true ifTrue:[ 
                Logger info:'      height: %1' with:value 
            ].

            ^ self
        ].
        (tagType == 258) ifTrue:[
            "bitspersample"  
             bitsPerSample := valueArray.

            Verbose == true ifTrue:[ 
                Logger info:'      bitspersample: %1' with:valueArray 
            ].

            ^ self
        ].
        (tagType == 259) ifTrue:[
            "/ compression
            "/      NONE            -> 1
            "/      CCITTRLE        -> 2
            "/      CCITTFAX3       -> 3
            "/      CCITTFAX4       -> 4
            "/      LZW             -> 5
            "/      OJPEG           -> 6 (old style jpeg)
            "/      JPEG            -> 7 (new style jpeg)
            "/      ADOBE_DEFLATE   -> 8
            "/      JBIG            -> 9 (ITU-T T85)
            "/      JBIG            -> 10 (ITU-T T43)

            "/      NEXT            -> 32766 (NeXT 2-bit encoding)
            "/      CCITTRLEW       -> 32771
            "/      PACKBITS        -> 32773
            "/      THUNDERSCAN     -> 32809 (ThunderScan 4-bit encoding)
            "/      IT8CTPAD        -> 32895  
            "/      IT8LW           -> 32896  
            "/      IT8MP           -> 32897  
            "/      IT8BL           -> 32898  
            "/      PIXARFILM       -> 32908
            "/      PIXARLOG        -> 32909 (Pixar companded 11-bit ZIP encoding)
            "/      DEFLATE         -> 32946 (PKZIP-style Deflate encoding)
            "/      DCS             -> 32947 (kodac)
            "/      JBIG            -> 34661
            "/      SGI32           -> 34676 (SGI 32-bit Log Luminance encoding)
            "/      SGI24           -> 34677 (SGI 24-bit Log Luminance encoding)
            "/      JPEG2000        -> 34712 JPEG2000
            "/      NIKON_NEF       -> 34713 
            "/      JBIG2           -> 34715 
            compression := value.

            Verbose == true ifTrue:[
                |s|
                s := (Dictionary withKeyValuePairs:
                        #( (1 uncompressed)
                            (2 CCITTRLE)
                            (3 CCITTFAX3)
                            (4 CCITTFAX4)
                            (5 LZW)
                            (6 OJPEG)
                            (7 JPEG)
                            (8 ADOBE_DEFLATE)
                            (9 JBIG_T85)
                            (10 JBIG_T43)
                            (32766 NEXT)
                            (32771 CCITTRLEW)
                            (32773 PACKBITS)
                            (32809 THUNDERSCAN)
                            (32895 IT8CTPAD)
                            (32896 IT8LW)
                            (32897 IT8MP)
                            (32898 IT8BL)
                            (32908 PIXARFILM)
                            (32909 PIXARLOG)
                            (32946 DEFLATE)
                            (32947 DCS)
                            (34661 JBIG)
                            (34676 SGI32)
                            (34677 SGI24)
                            (34712 JPEG2000)
                            (34713 NIKON_NEF)
                            (34715 JBIG2) 
                        )) at:value ifAbsent:'???'.
                Logger info:'      compression: %1 (=%2)' with:value with:s.
            ].
            ^ self
        ].

        (tagType == 262) ifTrue:[
            "photometric"
            |p|

            (value between:0 and:10) ifTrue:[
                p := 
                    #(
                        whiteIs0            "/  0 - grayscale or monochrome; faxes
                        blackIs0            "/  1 - grayscale or monochrome; faxes
                        rgb                 "/  2
                        palette             "/  3
                        transparencyMask    "/  4
                        cmyk                "/  5 - color separations
                        YCbCr               "/  6 - CCIR 601
                        nil                 "/  7
                        CIElab              "/  8 - 1976 CIE L*a*b*
                        ICClab              "/  9 - ICC L*a*b*
                        ITUlab              "/ 10 - see ITO-T- Rec T42 (RFC 2301)
                    ) at:(value + 1)    
            ] ifFalse:[
                (value == 32803) ifTrue:[
                    p := #ColorFilterArray    "/ camera rw format
                ].
                (value == 32844) ifTrue:[
                    p := #PixarLogL   
                ].
                (value == 32845) ifTrue:[
                    p := #PixarLogLuv    
                ].
                (value == 34892) ifTrue:[
                    p := #LinearRaw           "/ camera rw format
                ].
            ].
            photometric := p.
            Verbose == true ifTrue:[ 
                Logger info:'      photometric: %1 (=%2)' with:value with:photometric
            ].
            ^ self
        ].
        (tagType == 263) ifTrue:[
            "/ Thresholding
            "/      BILEVEL         -> 1
            "/      HALFTONE        -> 2
            "/      ERRORDIFFUSE    -> 3

            "thresholding := value."

            "/ 'thresholding ' print. value printNewline.

            ^ self
        ].
        (tagType == 264) ifTrue:[
            "CellWidth"
            "/ 'cellWidth ' print. value printNewline.
            metaData at:#CellWidth put:value.
            ^ self
        ].
        (tagType == 265) ifTrue:[
            "CellLength"
            "/ 'cellLength ' print. value printNewline.
            metaData at:#CellLength put:value.
            ^ self
        ].
        (tagType == 266) ifTrue:[
            "fillOrder"
            (value == FILLORDER_MSB2LSB ) ifTrue:[
                fillOrder := #msb
            ] ifFalse:[
                (value == FILLORDER_LSB2MSB) ifTrue:[
                    fillOrder := #lsb
                ] ifFalse:[
                    fillOrder := nil
                ]
            ].
            Verbose == true ifTrue:[ 
                Logger info:'      fillorder: %1' with:value 
            ].
            ^ self
        ].
        (tagType == 269) ifTrue:[
            "documentName - info only"
            metaData at:#DocumentName put:value.
            Verbose == true ifTrue:[ 
                Logger info:'      documentName: %1' with:value 
            ].
            ^ self
        ].
        (tagType == 270) ifTrue:[
            "imageDescription - info only"
            metaData at:#ImageDescription put:value.
            Verbose == true ifTrue:[ 
                Logger info:'      imageDescription: %1' with:value 
            ].
            ^ self
        ].
        (tagType == 271) ifTrue:[
            "make - info only"
            metaData at:#Make put:value.
            Verbose == true ifTrue:[ 
                Logger info:'      make: %1' with:value 
            ].
            ^ self
        ].
        (tagType == 272) ifTrue:[
            "model - info only"
            metaData at:#Model put:value.
            Verbose == true ifTrue:[ 
                Logger info:'      model: %1' with:value
            ].
            ^ self
        ].
        (tagType == 273) ifTrue:[
            "stripOffsets"
            stripOffsets := valueArray.
            Verbose == true ifTrue:[ 
                Logger info:'      stripOffsets: %1' with:valueArray 
            ].
            ^ self
        ].
        (tagType == 274) ifTrue:[
            "Orientation"

            orientation :=
                            #( nil          "/ 1 normal (topLeft)
                               hFlip        "/ 2 horizontal flip
                               hvFlip       "/ 3 horizontal & vertical flip
                               vFlip        "/ 4 vertical flip
                               rot90ccw     "/ 5 rot 90' counter clock-wise
                               rot90        "/ 6 rot 90' clock-wise
                               rot90flip    "/ 7 rot 90' & flip
                               rot90ccwFlip "/ 8 rot 90' ccw & flip
                             ) at:value ifAbsent:#unsupported.
            metaData at:#Orientation put:value.
            Verbose == true ifTrue:[ 
                Logger info:'      orientation: %1 (=%2)' with:value with:(orientation ? #normal)
            ].
            ^ self
        ].
        (tagType == 277) ifTrue:[
            samplesPerPixel := value.
            Verbose == true ifTrue:[ 
                Logger info:'      samplesperpixel: %1' with:value 
            ].
            ^ self
        ].
        (tagType == 278) ifTrue:[
            rowsPerStrip := value.
            Verbose == true ifTrue:[ 
                Logger info:'      rowsPerStrip: %1' with:value 
            ].
            ^ self
        ].
        (tagType == 279) ifTrue:[
            "stripbytecount"
            stripByteCounts := valueArray.
            "/        'stripByteCounts Array(' print. 
            "/        stripByteCounts size print.
            "/        ')' printNewline.
            Verbose == true ifTrue:[ 
                Logger info:'      stripByteCounts: %1' with:valueArray 
            ].
            ^ self
        ].
        (tagType == 280) ifTrue:[
            "/ minSampleValue
            metaData at:#MinSampleValue put:value.
            Verbose == true ifTrue:[ 
                Logger info:'      minSampleValue: %1' with:value 
            ].
            ^ self
        ].
        (tagType == 281) ifTrue:[
            "/ maxSampleValue
            metaData at:#MaxSampleValue put:value.
            Verbose == true ifTrue:[ 
                Logger info:'      maxSampleValue: %1' with:value 
            ].
            ^ self
        ].
        (tagType == 282) ifTrue:[
            "/ xResolution
            metaData at:#ResolutionX put:value.
            Verbose == true ifTrue:[ 
                Logger info:'      xResolution: %1 (%2)' with:value with:value asFloat 
            ].
            ^ self
        ].
        (tagType == 283) ifTrue:[
            "/ yResolution
            metaData at:#ResolutionY put:value.
            Verbose == true ifTrue:[ 
                Logger info:'      yResolution: %1 (%2)' with:value with:value asFloat 
            ].
            ^ self
        ].
        (tagType == 284) ifTrue:[
            (value == 1) ifTrue:[
                planarConfiguration := PLANARCONFIG_CONTIG
            ] ifFalse:[
                (value == 2) ifTrue:[
                    planarConfiguration := PLANARCONFIG_SEPARATE
                ] ifFalse:[
                    planarConfiguration := nil
                ]
            ].
            Verbose == true ifTrue:[ 
                Logger info:'      planarConfiguration: %1' with:value 
            ].
            ^ self
        ].
        (tagType == 285) ifTrue:[
            "/ 'pageName ' print. value printNewline.
            metaData at:#PageName put:value.
            ^ self
        ].
        (tagType == 286) ifTrue:[
            "/ xPosition
            metaData at:#PositionX put:value.
            Verbose == true ifTrue:[ 
                Logger info:'      xPosition: %1' with:value 
            ].
            ^ self
        ].
        (tagType == 287) ifTrue:[
            "/ yPosition
            metaData at:#PositionY put:value.
            Verbose == true ifTrue:[ 
                Logger info:'      yPosition: %1' with:value 
            ].
            ^ self
        ].
        (tagType == 288) ifTrue:[
            "/ 'freeOffsets ' print. value printNewline.
            ^ self
        ].
        (tagType == 289) ifTrue:[
            "/ 'freeByteCounts ' print. value printNewline.
            ^ self
        ].
        (tagType == 290) ifTrue:[
            "/ 'grayResponceUnit' print. value printNewline.
            metaData at:#GrayResponceUnit put:value.
            ^ self
        ].
        (tagType == 291) ifTrue:[
            "/ 'grayResponceCurve' print. value printNewline.
            metaData at:#GrayResponceCurve put:value.
            ^ self
        ].
        (tagType == 292) ifTrue:[
            "/ group3options (now called T4Options)
            "/      2DENCODING      -> 1
            "/      UNCOMPRESSED    -> 2
            "/      FILLBITS        -> 4

            group3options := value.
            Verbose == true ifTrue:[ 
                Logger info:'      group3options: %1' with:value 
            ].
            ^ self
        ].
        (tagType == 293) ifTrue:[
            "/ group4options (now called T6Options)
            "/      UNCOMPRESSED    -> 2

            "/ group4options := value.
            Verbose == true ifTrue:[ 
                Logger info:'      group4options: %1' with:value 
            ].
            ^ self
        ].
        (tagType == 296) ifTrue:[
            "resolutionunit"

            "/        (value == 1) ifTrue:[
            "/            'res-unit pixel' printNewline
            "/        ] ifFalse:[
            "/            (value == 2) ifTrue:[
            "/                'res-unit inch' printNewline
            "/            ] ifFalse:[
            "/                (value == 3) ifTrue:[
            "/                    'res-unit mm' printNewline
            "/                ] ifFalse:[
            "/                    'res-unit invalid' printNewline
            "/                ]
            "/            ]
            "/        ].
            metaData at:#ResolutionUnit put:value.
            Verbose == true ifTrue:[ 
                Logger info:'      resolutionUnit: %1' with:value 
            ].
            ^ self
        ].
        (tagType == 297) ifTrue:[
            "/ 'pageNumber ' print. value printNewline.
            metaData at:#PageNumber put:value.
            Verbose == true ifTrue:[ 
                Logger info:'      PageNumber: %1' with:value 
            ].
            ^ self
        ].
    ].
    
    (tagType between:300 and:399) ifTrue:[
        (tagType == 300) ifTrue:[
            "/ 'colorResponceUnit' print. value printNewline.
            metaData at:#ColorResponceUnit put:value.
            ^ self
        ].
        (tagType == 301) ifTrue:[
            "/ 'colorResponceCurve' print. value printNewline.
            metaData at:#ColorResponceCurve put:value.
            ^ self
        ].
        (tagType == 305) ifTrue:[
            "software - info only"
            metaData at:#Software put:value.
            Verbose == true ifTrue:[ 
                Logger info:'      software: %1' with:value
            ].
            ^ self
        ].
        (tagType == 306) ifTrue:[
            "dateTime - info only"
            metaData at:#DateTime put:value.
            Verbose == true ifTrue:[ 
                Logger info:'      dateTime: %1' with:value
            ].
            ^ self
        ].
        (tagType == 315) ifTrue:[
            "artist - info only"
            metaData at:#Artist put:value.
            Verbose == true ifTrue:[ 
                Logger info:'      artist: %1' with:value
            ].
            ^ self
        ].
        (tagType == 316) ifTrue:[
            "host computer - info only"
            metaData at:#HostComputer put:value.
            Verbose == true ifTrue:[ 
                Logger info:'      host: %1' with:value
            ].
            ^ self
        ].
        (tagType == 317) ifTrue:[
            "/ predictor
            "/ 1 -> no predictor
            "/ 2 -> horiz. difference (see tiff spec 6.0)
            "/ 3 -> flt pnt (see adobe tech notes)
            "/ 34892 -> horiz difference x2
            "/ 34893 -> horiz difference x4
            "/ 34894 -> flt pnt x2
            "/ 34895 -> flt pnt x4
            predictor := value.
            Verbose == true ifTrue:[ 
                Logger info:'      predictor: %1' with:value
            ].
            ^ self
        ].
        (tagType == 318) ifTrue:[
            "/ whitePoint
            metaData at:#WhitePoint put:value.
            ^ self
        ].
        (tagType == 319) ifTrue:[
            "/ primaryChromatics
            metaData at:#PrimaryChromatics put:value.
            ^ self
        ].
        (tagType == 320) ifTrue:[
            "/ 'colorMap (size=' print. valueArray size print. ')' printNewline.

            "
             the tiff colormap contains 16bit values;
             our colormap expects 8bit values
            "
            n := valueArray size // 3.

            rV := ByteArray uninitializedNew:n.
            gV := ByteArray uninitializedNew:n.
            bV := ByteArray uninitializedNew:n.
            scaleFactor := 255.0 / 16rFFFF.
            i2 := n+1.
            i3 := 2*n+1.
            1 to:n do:[:vi |
                val := ((valueArray at:vi) * scaleFactor) rounded.
                rV at:vi put:val.
                val := ((valueArray at:i2) * scaleFactor) rounded.
                gV at:vi put:val.
                val := ((valueArray at:i3) * scaleFactor) rounded.
                bV at:vi put:val.
                i2 := i2 + 1.
                i3 := i3 + 1.
            ].
            colorMap := MappedPalette redVector:rV greenVector:gV blueVector:bV.
            Verbose == true ifTrue:[ 
                Logger info:'      colormap: ...'
            ].
            ^ self
        ].
        (tagType == 321) ifTrue:[
            "/ halftonehints
            metaData at:#HalftoneHints put:value.
            ^ self
        ].
        (tagType == 322) ifTrue:[
            "/ tilewidth
            tileWidth := value.
            Verbose == true ifTrue:[ 
                Logger info:'      tileWidth: %1' with:value 
            ].
            (tileWidth \\ 16) == 0 ifFalse:[
                Logger warning:'TIFFReader: tile width is not a multiple of 16'.
            ].    
            ^ self
        ].
        (tagType == 323) ifTrue:[
            "/ tilelength (height)
            tileLength := value.
            Verbose == true ifTrue:[ 
                Logger info:'      tileLength: %1' with:value 
            ].
            (tileLength \\ 16) == 0 ifFalse:[
                Logger warning:'TIFFReader: tile height is not a multiple of 16'.
            ].    
            ^ self
        ].
        (tagType == 324) ifTrue:[
            "/ tileoffsets
            tileOffsets := valueArray.
            Verbose == true ifTrue:[ 
                Logger info:'      tileOffsets: %1' with:valueArray 
            ].
            ^ self
        ].
        (tagType == 325) ifTrue:[
            "/ tilebytecounts
            tileByteCounts := valueArray.
            Verbose == true ifTrue:[ 
                Logger info:'      tileByteCounts: %1' with:valueArray 
            ].
            ^ self
        ].
        (tagType == 326) ifTrue:[
            "/ badFaxLines
            Verbose == true ifTrue:[ 
                Logger info:'      badFaxLines: %1' with:valueArray 
            ].
            ^ self
        ].
        (tagType == 327) ifTrue:[
            "CleanFaxData"
            Verbose == true ifTrue:[ 
                "/        'cleanfaxdata' print. value printNewline.
                "/        (value == 0) ifTrue:[
                "/            'no lines with incorrect pixel counts' printNewline
                "/        ] ifFalse:[
                "/            (value == 1) ifTrue:[
                "/                'incorrect lines were regenerated' printNewline
                "/            ] ifFalse:[
                "/                (value == 2) ifTrue:[
                "/                    'incorrect lines were not regenerated' printNewline
                "/                ] ifFalse:[
                "/                    'cleanfaxdata invalid' printNewline
                "/                ]
                "/            ]
                "/        ].
                Logger info:'      cleanfaxdata: %1' with:value  
            ].
            ^ self
        ].
        (tagType == 328) ifTrue:[
            "/ consecutiveBadFaxLines
            Verbose == true ifTrue:[ 
                Logger info:'      consecutiveBadFaxLines: %1' with:valueArray 
            ].
            ^ self
        ].
        (tagType == 330) ifTrue:[
            "/ subifd
            Verbose == true ifTrue:[ 
                Logger info:'      subifd: %1' with:valueArray 
            ].
            subIfds := valueArray. 
            ^ self
        ].
        (tagType == 332) ifTrue:[
            "/ 'ink set' print. value printNewline.
            ^ self
        ].
        (tagType == 333) ifTrue:[
            "/ 'ink names' print. value printNewline.
            metaData at:#IncNames put:value.
            ^ self
        ].
        (tagType == 334) ifTrue:[
            "/ 'numinks' print. value printNewline.
            ^ self
        ].
        (tagType == 336) ifTrue:[
            "/ 'dot range' print. value printNewline.
            ^ self
        ].
        (tagType == 337) ifTrue:[
            "/ 'target printer' print. value printNewline.
            ^ self
        ].
        (tagType == 338) ifTrue:[
            "/ 'extrasamples' print. value printNewline.
            Verbose == true ifTrue:[ 
                Logger info:'      extrasamples: %1' with:value 
            ].
            ^ self
        ].
        (tagType == 339) ifTrue:[
            "/ 'sample format' print. value printNewline.
            Verbose == true ifTrue:[ 
                Logger info:'      sample format: %1 (=%2)' 
                            with:value 
                            with:(#( uint int float undef ) at:value ifAbsent:'???')
            ].
            sampleFormat := value.
            ^ self
        ].
        (tagType == 340) ifTrue:[
            "/ 'min sample value' print. value printNewline.
            Verbose == true ifTrue:[ 
                Logger info:'      min sample value: %1' with:value 
            ].
            minSampleValue := value.
            ^ self
        ].
        (tagType == 341) ifTrue:[
            "/ 'max sample value' print. value printNewline.
            Verbose == true ifTrue:[ 
                Logger info:'      max sample value: %1' with:value 
            ].
            maxSampleValue := value.
            ^ self
        ].
        (tagType == 342) ifTrue:[
            "/ 'transfer range' print. value printNewline.
            ^ self
        ].
        (tagType == 343) ifTrue:[
            "/ 'clip path' print. value printNewline.
            ^ self
        ].
        (tagType == 344) ifTrue:[
            "/ 'xclip path units' print. value printNewline.
            ^ self
        ].
        (tagType == 345) ifTrue:[
            "/ 'yclip path units' print. value printNewline.
            ^ self
        ].
        (tagType == 346) ifTrue:[
            "/ 'indexed' print. value printNewline.
            ^ self
        ].
        (tagType == 347) ifTrue:[
            "/ 'jpegtables' print. value printNewline.
            ^ self
        ].
        (tagType == 351) ifTrue:[
            "/ opiproxy
            ^ self
        ].
    ].

    (tagType between:400 and:499) ifTrue:[
        (tagType == 400) ifTrue:[
            "/ 'GlobalParametersIFD' print. value printNewline.
            ^ self
        ].
        (tagType == 401) ifTrue:[
            "/ 'ProfileType' print. value printNewline.
            ^ self
        ].
        (tagType == 402) ifTrue:[
            "/ 'FaxProfile' print. value printNewline.
            ^ self
        ].
        (tagType == 403) ifTrue:[
            "/ 'CodingMethods' print. value printNewline.
            ^ self
        ].
        (tagType == 404) ifTrue:[
            "/ 'VersionYear' print. value printNewline.
            ^ self
        ].
        (tagType == 405) ifTrue:[
            "/ 'ModeNumber' print. value printNewline.
            ^ self
        ].
        (tagType == 433) ifTrue:[
            "/ 'Decode' print. value printNewline.
            ^ self
        ].
        (tagType == 434) ifTrue:[
            "/ 'DefaultImageColor' print. value printNewline.
            ^ self
        ].
    ].
    
    (tagType between:500 and:599) ifTrue:[
        "/ obsolete JPEG tags
        (tagType == 512) ifTrue:[
            "/ 'jpeg proc' print. value printNewline.
            ^ self
        ].
        (tagType == 513) ifTrue:[
            "/ 'jpeg proc' print. value printNewline.
            ^ self
        ].
        (tagType == 514) ifTrue:[
            "/ 'jpeg ifByteCount' print. value printNewline.
            ^ self
        ].
        (tagType == 515) ifTrue:[
            "/ 'jpeg restartInterval' print. value printNewline.
            ^ self
        ].
        (tagType == 517) ifTrue:[
            "/ 'jpeg glossLessPredictors' print. value printNewline.
            ^ self
        ].
        (tagType == 518) ifTrue:[
            "/ 'jpeg pointTransform' print. value printNewline.
            ^ self
        ].
        (tagType == 519) ifTrue:[
            "/ 'jpeg qTables' print. value printNewline.
            ^ self
        ].
        (tagType == 520) ifTrue:[
            "/ 'jpeg dcTables' print. value printNewline.
             ^ self
        ].
        (tagType == 521) ifTrue:[
            "/ 'jpeg acTables' print. value printNewline.
            ^ self
        ].


        (tagType == 529) ifTrue:[
            "/ ycbr coeff
            Verbose == true ifTrue:[ 
                Logger info:'      ycbr coeff: %1' with:value 
            ].
            ^ self
        ].
        (tagType == 530) ifTrue:[
            "/ ycbr subsampling
            Verbose == true ifTrue:[ 
                Logger info:'      ycbr subsampling: %1' with:value 
            ].
            ^ self
        ].
        (tagType == 531) ifTrue:[
            "/ ycbr positioning
            Verbose == true ifTrue:[ 
                Logger info:'      ycbr positioning: %1' with:value 
            ].
            ^ self
        ].
        (tagType == 532) ifTrue:[
            "/ referenceBlackWhite
            Verbose == true ifTrue:[ 
                Logger info:'      referenceBlackWhite: %1' with:value 
            ].
            ^ self
        ].
        (tagType == 559) ifTrue:[
            "/ stripRowCounts
            "/ Defined in the Mixed Raster Content part of RFC 2301, 
            "/ used to replace RowsPerStrip for IFDs with variable-sized strips.
            Verbose == true ifTrue:[ 
                Logger info:'      stripRowCounts: %1' with:valueArray 
            ].
            stripRowCounts := valueArray.
            ^ self
        ].
    ].

    (tagType between:700 and:799) ifTrue:[
        (tagType == 700) ifTrue:[
            "XMP metadata (xml)"

            "/ In TIFF files, the XML Packet containing XMP metadata is pointed to
            "/ by an entry in the Image File Directory (IFD). That entry has a Tag
            "/ value of 700, as shown in Table 1.1, "TIFF IFD Directory Entry for
            "/ XML Packets
            
            Verbose == true ifTrue:[ 
                Logger info:'      XMLPACKET: %1' with:value asString
            ].
            decodeMetaTags == true ifTrue:[
                metaData at:#xmpData put:value asString.
            ].    
            ^ self
        ].
    ].
    
    (tagType between:18000 and:18999) ifTrue:[
        (tagType == 18246) ifTrue:[
            "/ Image Rating by windows
            ^ self
        ].
        (tagType == 18249) ifTrue:[
            "/ Image Rating Percent by windows
            ^ self
        ].
    ].

    (tagType between:32000 and:32999) ifTrue:[
        (tagType == 32781) ifTrue:[
            "/'imageid' print. value printNewline.
            ^ self
        ].
        (tagType == 32932) ifTrue:[
            "/'wang annotation' print. value printNewline.
            ^ self
        ].

        "/ Private Island graphics tags
        (tagType == 32953) ifTrue:[
            "/'ref points' print. value printNewline.
            ^ self
        ].
        (tagType == 32954) ifTrue:[
            "/ 'regionTagPoint' print. value printNewline.
            ^ self
        ].
        (tagType == 32955) ifTrue:[
            "/ 'regionWarpCorners' print. value printNewline.
            ^ self
        ].
        (tagType == 32956) ifTrue:[
            "/ 'regionAffine' print. value printNewline.
            ^ self
        ].


        "/ Private SGI tags
        (tagType == 32995) ifTrue:[
            "/ 'matteing' print. value printNewline.
            ^ self
        ].
        
        (tagType == 32996) ifTrue:[
            "/ datatype
            Verbose == true ifTrue:[ 
                Logger info:'      datatype: %1' with:value
            ].
            ^ self
        ].
        (tagType == 32997) ifTrue:[
            "/ imagedepth
            Verbose == true ifTrue:[ 
                Logger info:'      imagedepth: %1' with:value
            ].
            ^ self
        ].
        (tagType == 32998) ifTrue:[
            "/ tiledepth
            Verbose == true ifTrue:[ 
                Logger info:'      tiledepth: %1' with:value
            ].
            ^ self
        ].
    ].
    
    (tagType between:33000 and:33999) ifTrue:[
        "/ Private Pixar tags
        (tagType == 33300) ifTrue:[
            "/ 'image full width' print. value printNewline.
            ^ self
        ].
        (tagType == 33301) ifTrue:[
            "/ 'image full length' print. value printNewline.
            ^ self
        ].

        "/ Private Eastman Kodak tags
        (tagType == 33405) ifTrue:[
            "/ 'write serial number' print. value printNewline.
            ^ self
        ].

        (tagType == 33421) ifTrue:[
            "/ CFARepeatPatternDim - For camera raw files from sensors with CFA overlay
            Verbose == true ifTrue:[ 
                Logger info:'      CFARepeatPatternDim: %1' with:value
            ].
            ^ self
        ].
        (tagType == 33422) ifTrue:[
            "/ CFAPattern - For camera raw files from sensors with CFA overlay
            Verbose == true ifTrue:[ 
                Logger info:'      CFAPattern: %1' with:value
            ].
            ^ self
        ].
        (tagType == 33423) ifTrue:[
            "/ BatteryLevel - Encodes camera battery level at time of image capture
            ^ self
        ].
        (tagType == 33432) ifTrue:[
            "/ 'copyright' print. value printNewline.
            ^ self
        ].

        (tagType == 33550) ifTrue:[
            "/ 'geotiff modelpixelscaletag' print. value printNewline.
            ^ self
        ].

        (tagType == 33723) ifTrue:[
            "/ 'RICHTIFFIPTC' print. value printNewline.
            ^ self
        ].


        (tagType == 33920) ifTrue:[
            "/ 'geotiff IntergraphMatrixTag' print. value printNewline.
            ^ self
        ].
        (tagType == 33922) ifTrue:[
            "/ 'geotiff ModelTiepointTag' print. value printNewline.
            ^ self
        ].

    ].

    (tagType between:34000 and:34999) ifTrue:[
        (tagType == 34016) ifTrue:[
            "/ Site
            Verbose == true ifTrue:[ 
                Logger info:'      site: %1' with:value 
            ].
            ^ self
        ].
        (tagType == 34017) ifTrue:[
            "/ colorSequence
            ^ self
        ].
        (tagType == 34018) ifTrue:[
            "/ it8header
            ^ self
        ].
        (tagType == 34019) ifTrue:[
            "/ rasterPadding
            ^ self
        ].
        (tagType == 34020) ifTrue:[
            "/ bitsPerRunLength
            ^ self
        ].
        (tagType == 34021) ifTrue:[
            "/ bitsPerExtendedRunLength
            ^ self
        ].
        (tagType == 34022) ifTrue:[
            "/ colorTable
            ^ self
        ].
        (tagType == 34023) ifTrue:[
            "/ imageColorIndicator
            ^ self
        ].
        (tagType == 34024) ifTrue:[
            "/ backgroundColorIndicator
            ^ self
        ].
        (tagType == 34025) ifTrue:[
            "/ imageColorValue
            ^ self
        ].
        (tagType == 34026) ifTrue:[
            "/ backgroundColorValue
            ^ self
        ].
        (tagType == 34027) ifTrue:[
            "/ pixelIntensityRange
            ^ self
        ].
        (tagType == 34028) ifTrue:[
            "/ transparencyIndicator
            ^ self
        ].
        (tagType == 34029) ifTrue:[
            "/ colorCharacterization
            ^ self
        ].
        (tagType == 34030) ifTrue:[
            "/ hcUsage
            ^ self
        ].
        (tagType == 34031) ifTrue:[
            "/ trapIndicator
            ^ self
        ].
        (tagType == 34032) ifTrue:[
            "/ cmykEquivalent
            ^ self
        ].

        "/ Private Pixel magic
        (tagType == 34232) ifTrue:[
            "/ 'jbig options' print. value printNewline.
            ^ self
        ].

        (tagType == 34264) ifTrue:[
            "/ 'geotiff ModelTransformationTag' print. value printNewline.
            ^ self
        ].
        "/ private Photoshop
        (tagType == 34377) ifTrue:[
            "/ RICHTIFFIPTC
            "/ IPTC (International Press Telecommunications Council) metadata.
            "/ (see http://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata)
            decodeMetaTags == true ifTrue:[
                Verbose == true ifTrue:[ 
                    Logger info:'      RICHTIFFIPTC: %1' with:value
                ].
                self decodePhotoshopImageResourceBlock:value.
            ] ifFalse:[
                Verbose == true ifTrue:[ 
                    Logger info:'      skipped decoding of IPTC-PhotoMetadata'
                ].
            ].    
            ^ self
        ].

        (tagType == 34665) ifTrue:[
            "/ EXIFIFD
            Verbose == true ifTrue:[ 
                Logger info:'      EXIFIFD: %1' with:value 
            ].
            ^ self
        ].
        (tagType == 34675) ifTrue:[
            "/ 'ICCPROFILE' print. value printNewline.
            ^ self
        ].
        
        (tagType == 34732) ifTrue:[
            "/ 'ImageLayer' print. value printNewline.
            ^ self
        ].

        (tagType == 34735) ifTrue:[
            "/ 'geotiff GeoKeyDirectoryTag' print. value printNewline.
            ^ self
        ].
        (tagType == 34736) ifTrue:[
            "/ 'geotiff GeoDoubleParamsTag' print. value printNewline.
            ^ self
        ].
        (tagType == 34737) ifTrue:[
            "/ 'geotiff GeoAsciiParamsTag' print. value printNewline.
            ^ self
        ].

        
        (tagType == 34859) ifTrue:[
            "/ '???' print. value printNewline.
            "/ Verbose == true ifTrue:[ 
            "/     Logger info:'      ?: %1' with:value 
            "/ ].
            ^ self
        ].
        
        "/ More Private SGI
        (tagType == 34908) ifTrue:[
            "/ 'fax recv params' print. value printNewline.
            ^ self
        ].
        (tagType == 34909) ifTrue:[
            "/ 'fax subaddress' print. value printNewline.
            ^ self
        ].
        (tagType == 34910) ifTrue:[
            "/ 'fax recv time' print. value printNewline.
            ^ self
        ].
    ].

    (tagType between:36000 and:36999) ifTrue:[
        (tagType == 36867) ifTrue:[
            "/ '???' print. value printNewline.
            ^ self
        ].
    ].

    (tagType between:37000 and:37999) ifTrue:[
        (tagType == 37390) ifTrue:[
            "/ '???' print. value printNewline.
            ^ self
        ].
        (tagType == 37391) ifTrue:[
            "/ '???' print. value printNewline.
            ^ self
        ].
        (tagType == 37392) ifTrue:[
            "/ '???' print. value printNewline.
            ^ self
        ].
        (tagType == 37398) ifTrue:[
            "/ '???' print. value printNewline.
            ^ self
        ].
    ].

    (tagType between:42000 and:42999) ifTrue:[
        (tagType == 42112) ifTrue:[
            "/ 'GDAL_METADATA' print. value printNewline.
            Verbose == true ifTrue:[ 
                Logger info:'      GDAL_METADATA: %1' with:value
            ].
            ^ self
        ].
    ].

    "/ dng tags (see http://wwwimages.adobe.com/content/dam/Adobe/en/products/photoshop/pdfs/dng_spec_1.4.0.0.pdf)
    (tagType between:50000 and:50999) ifTrue:[
        (tagType == 50706) ifTrue:[
            "/ DNGVersion
            Verbose == true ifTrue:[ 
                Logger info:'      DNGVersion: %1' with:value
            ].
            isDNGImage := true.
            ^ self
        ].
        (tagType == 50707) ifTrue:[
            "/ DNGBackwardVersion
            ^ self
        ].
        (tagType == 50708) ifTrue:[
            "/ UniqueCameraModel
            Verbose == true ifTrue:[ 
                Logger info:'      UniqueCameraModel: %1' with:value
            ].
            ^ self
        ].
        (tagType == 50709) ifTrue:[
            "/ LocalizedCameraModel
            Verbose == true ifTrue:[ 
                Logger info:'      LocalizedCameraModel: %1' with:value
            ].
            ^ self
        ].
        (tagType == 50710) ifTrue:[
            "/ 'CFAPlaneColor' print. value printNewline.
            ^ self
        ].
        (tagType == 50711) ifTrue:[
            "/ 'CFALayout' print. value printNewline.
            ^ self
        ].
        (tagType == 50712) ifTrue:[
            "/ 'LinearizationTable' print. value printNewline.
            ^ self
        ].
        (tagType == 50713) ifTrue:[
            "/ 'BlackLevelRepeatDim' print. value printNewline.
            ^ self
        ].
        (tagType == 50714) ifTrue:[
            "/ 'BlackLevel' print. value printNewline.
            ^ self
        ].
        (tagType == 50715) ifTrue:[
            "/ 'BlackLevelDeltaH' print. value printNewline.
            ^ self
        ].
        (tagType == 50716) ifTrue:[
            "/ 'BlackLevelDeltaV' print. value printNewline.
            ^ self
        ].
        (tagType == 50717) ifTrue:[
            "/ 'WhiteLevel' print. value printNewline.
            ^ self
        ].
        (tagType == 50718) ifTrue:[
            "/ 'DefaultScale' print. value printNewline.
            ^ self
        ].
        (tagType == 50719) ifTrue:[
            "/ 'DefaultCropOrigin' print. value printNewline.
            ^ self
        ].
        (tagType == 50720) ifTrue:[
            "/ 'DefaultCropSize' print. value printNewline.
            ^ self
        ].
        (tagType == 50721) ifTrue:[
            "/ 'ColorMatrix1' print. value printNewline.
            ^ self
        ].
        (tagType == 50722) ifTrue:[
            "/ 'ColorMatrix2' print. value printNewline.
            ^ self
        ].
        (tagType == 50723) ifTrue:[
            "/ 'CameraCalibrarion1' print. value printNewline.
            ^ self
        ].
        (tagType == 50724) ifTrue:[
            "/ 'CameraCalibrarion2' print. value printNewline.
            ^ self
        ].
        (tagType == 50725) ifTrue:[
            "/ 'ReductionMatrix1' print. value printNewline.
            ^ self
        ].
        (tagType == 50726) ifTrue:[
            "/ 'ReductionMatrix2' print. value printNewline.
            ^ self
        ].
        (tagType == 50727) ifTrue:[
            "/ 'AnalogBalance' print. value printNewline.
            ^ self
        ].
        (tagType == 50728) ifTrue:[
            "/ 'AsShotNeutral' print. value printNewline.
            ^ self
        ].
        (tagType == 50729) ifTrue:[
            "/ 'AsShotWhiteXY' print. value printNewline.
            ^ self
        ].
        (tagType == 50730) ifTrue:[
            "/ 'BaselineExposure' print. value printNewline.
            ^ self
        ].
        (tagType == 50731) ifTrue:[
            "/ 'BaselineNoise' print. value printNewline.
            ^ self
        ].
        (tagType == 50732) ifTrue:[
            "/ 'BaselineSharpness' print. value printNewline.
            ^ self
        ].
        (tagType == 50733) ifTrue:[
            "/ 'ByerGreenSplit' print. value printNewline.
            ^ self
        ].
        (tagType == 50734) ifTrue:[
            "/ 'LinearResponseLimit' print. value printNewline.
            ^ self
        ].
        (tagType == 50735) ifTrue:[
            "/ 'CameraSerialNumber' print. value printNewline.
            Verbose == true ifTrue:[ 
                Logger info:'      CameraSerialNumber: %1' with:value
            ].
            ^ self
        ].
        (tagType == 50736) ifTrue:[
            "/ 'LensInfo' print. value printNewline.
            ^ self
        ].
        (tagType == 50737) ifTrue:[
            "/ 'ChromaBlurRadius' print. value printNewline.
            ^ self
        ].
        (tagType == 50738) ifTrue:[
            "/ 'AntiAliasStrength' print. value printNewline.
            ^ self
        ].
        (tagType == 50739) ifTrue:[
            "/ 'ShadowScale' print. value printNewline.
            ^ self
        ].
        (tagType == 50740) ifTrue:[
            "/ 'DNGPrivateData' print. value printNewline.
            ^ self
        ].
        (tagType == 50741) ifTrue:[
            "/ 'MakerNoteSafety' print. value printNewline.
            ^ self
        ].
        
        (tagType == 50778) ifTrue:[
            "/ 'CalibrationIlluminant1' print. value printNewline.
            ^ self
        ].
        (tagType == 50779) ifTrue:[
            "/ 'CalibrationIlluminant2' print. value printNewline.
            ^ self
        ].
        (tagType == 50780) ifTrue:[
            "/ 'BestQualityScale' print. value printNewline.
            ^ self
        ].
        (tagType == 50781) ifTrue:[
            "/ 'RawDataUniqueID' print. value printNewline.
            ^ self
        ].
        
        (tagType == 50827) ifTrue:[
            "/ 'OriginalRawFileName' print. value printNewline.
            ^ self
        ].
    ].
    
"/
"/ 'TIFFReader: tag:' print. tagType print. ' typ:' print. numberType print.
"/ ' len:' print. length print. ' offs:' print. offset print. 
"/ ' val:' print. value print. ' valArr:' print. valueArray printNewline.  
"/
    'TIFFReader [warning]: unknown tag type ' errorPrint. tagType errorPrintCR

    "Modified (format): / 23-05-2017 / 16:12:58 / mawalch"
    "Modified: / 28-08-2017 / 00:48:37 / cg"
!

positionToStrip:stripNr
    inStream position:(stripOffsets at:stripNr).
!

positionToTile:tileNr
    inStream position:((metaData at:#'TileOffsets') at:tileNr).

    "Created: / 25-08-2017 / 13:43:30 / cg"
!

readBytes:n signed:isSigned
    "read n 8bit signed or unsigned integers and return them in an array or byteArray"

    |oldPos offset bytes nInline|

    nInline := isBigTiff ifTrue:[8] ifFalse:[4].

    n == 0 ifTrue:[
        "/ even in this case, one WORD is to be skipped.
        inStream skip:nInline.
        ^ ''
    ].

    bytes := (isSigned ifTrue:[Array] ifFalse:[ByteArray]) new:n.
    (n <= nInline) ifTrue:[
        isSigned ifTrue:[
            1 to:n do:[:i | bytes at:i put:(inStream nextSignedByte) ].
        ] ifFalse:[
            inStream nextBytes:n into:bytes.
        ].
        (n < nInline) ifTrue:[
            inStream skip:(nInline - n).
        ]
    ] ifFalse:[
        offset := inStream nextInt32MSB:(byteOrder ~~ #lsb).
        oldPos := inStream position.
        inStream position:offset.
        isSigned ifTrue:[
            1 to:n do:[:i | bytes at:i put:(inStream nextSignedByte) ].
        ] ifFalse:[
            inStream nextBytes:n into:bytes.
        ].
        inStream position:oldPos
    ].
    ^ bytes

    "Modified: / 27-08-2017 / 19:56:23 / cg"
!

readChars:n
    "read n characters and return them in a string"

    |oldPos offset string nInline|

    nInline := isBigTiff ifTrue:[8] ifFalse:[4].

    n == 0 ifTrue:[
        "/ even in this case, one WORD is to be skipped.
        inStream skip:nInline.
        ^ ''
    ].

    string := String new:(n - 1).
    (n <= nInline) ifTrue:[
        inStream nextBytes:(n - 1) into:string.
        inStream next. "/ skip the 0-byte.

        (n < nInline) ifTrue:[
            inStream skip:(nInline - n).
        ]
    ] ifFalse:[
        offset := inStream nextInt32MSB:(byteOrder == #msb).
        oldPos := inStream position.
        inStream position:offset.
        inStream nextBytes:(n - 1) into:string.
        inStream position:oldPos
    ].
    ^ string

    "Modified: / 27-08-2017 / 19:55:15 / cg"
!

readDoubles:nFloats
    "read nFloats IEEE 64bit doubles and return them in an array"

    |oldPos offset values val msb 
     n "{ Class: SmallInteger }" |

    n := nFloats.

    msb := byteOrder ~~ #lsb.
    values := DoubleArray basicNew:n.
    (n == 1) ifTrue:[
        val := Float readBinaryIEEEDoubleFrom:inStream MSB:msb.
        values at:1 put:val.
    ] ifFalse:[
        offset := inStream nextInt32MSB:msb.
        oldPos := inStream position.
        inStream position:offset.
        1 to:n do:[:index |
            val := Float readBinaryIEEEDoubleFrom:inStream MSB:msb.
            values at:index put:val
        ].
        inStream position:oldPos
    ].
    ^ values

    "Modified: / 24-08-2017 / 23:28:22 / cg"
!

readFloats:nFloats
    "read nFloats IEEE 32bit floats and return them in an array"

    |oldPos offset values val val1 val2 msb 
     n "{ Class: SmallInteger }" |

    n := nFloats.

    msb := byteOrder ~~ #lsb.
    values := FloatArray basicNew:n.
    (isBigTiff and:[ n == 2 ]) ifTrue:[
        val1 := ShortFloat readBinaryIEEESingleFrom:inStream MSB:msb.
        val2 := ShortFloat readBinaryIEEESingleFrom:inStream MSB:msb.
        values at:1 put:val1.
        n == 2 ifTrue:[
            values at:2 put:val2.
        ].
    ] ifFalse:[    
        (n == 1) ifTrue:[
            val := ShortFloat readBinaryIEEESingleFrom:inStream MSB:msb.
            values at:1 put:val.
        ] ifFalse:[
            offset := inStream nextInt32MSB:msb.
            oldPos := inStream position.
            inStream position:offset.
            1 to:n do:[:index |
                val := ShortFloat readBinaryIEEESingleFrom:inStream MSB:msb.
                values at:index put:val
            ].
            inStream position:oldPos
        ].
    ].
    ^ values

    "Modified: / 24-08-2017 / 23:36:39 / cg"
!

readFracts:nFracts signed:isSigned
    "read nFracts fractions (2 32bit words) and return them in an array"

    |oldPos offset values numerator denominator msb
     n "{ Class: SmallInteger }" |

    n := nFracts.

    msb := byteOrder ~~ #lsb.
    values := Array basicNew:n.
    offset := inStream nextInt32MSB:msb.
    oldPos := inStream position.
    inStream position:offset.
    1 to:n do:[:index |
        numerator := isSigned ifTrue:[inStream nextInt32MSB:msb] ifFalse:[inStream nextUnsignedInt32MSB:msb].
        denominator := isSigned ifTrue:[inStream nextInt32MSB:msb] ifFalse:[inStream nextUnsignedInt32MSB:msb].
        values at:index put:(Fraction numerator:numerator denominator:denominator)
    ].
    inStream position:oldPos.
    ^ values
!

readLong8s:nLongs signed:isSigned
    "read nLongs signed or unsigned long8 numbers (64bit) and return them in an array"

    |oldPos offset values val msb 
     n "{ Class: SmallInteger }" |

    n := nLongs.

    msb := byteOrder ~~ #lsb.
    values := Array basicNew:n.
    (n == 1) ifTrue:[
        val := isSigned ifTrue:[inStream nextInt64MSB:msb] ifFalse:[inStream nextUnsignedInt64MSB:msb].
        values at:1 put:val.
    ] ifFalse:[
        offset := inStream nextInt64MSB:msb.
        oldPos := inStream position.
        inStream position:offset.
        1 to:n do:[:index |
            val := isSigned ifTrue:[inStream nextInt64MSB:msb] ifFalse:[inStream nextUnsignedInt64MSB:msb].
            values at:index put:val
        ].
        inStream position:oldPos
    ].
    ^ values

    "Created: / 24-08-2017 / 22:01:26 / cg"
!

readLongs:nLongs signed:isSigned
    "read nLongs signed or unsigned long numbers (32bit) and return them in an array"

    |oldPos offset values val val1 val2 msb 
     n "{ Class: SmallInteger }" |

    n := nLongs.

    msb := byteOrder ~~ #lsb.
    values := Array basicNew:n.
    (isBigTiff and:[ n <= 2 ]) ifTrue:[
        val1 := isSigned ifTrue:[inStream nextInt32MSB:msb] ifFalse:[inStream nextUnsignedInt32MSB:msb].
        val2 := isSigned ifTrue:[inStream nextInt32MSB:msb] ifFalse:[inStream nextUnsignedInt32MSB:msb].
        values at:1 put:val1.
        n == 2 ifTrue:[
            values at:2 put:val2.
        ].
    ] ifFalse:[
        (n == 1) ifTrue:[
            val := isSigned ifTrue:[inStream nextInt32MSB:msb] ifFalse:[inStream nextUnsignedInt32MSB:msb].
            values at:1 put:val.
        ] ifFalse:[
            offset := inStream nextInt32MSB:msb.
            oldPos := inStream position.
            inStream position:offset.
            1 to:n do:[:index |
                val := isSigned ifTrue:[inStream nextInt32MSB:msb] ifFalse:[inStream nextUnsignedInt32MSB:msb].
                values at:index put:val
            ].
            inStream position:oldPos
        ].
    ].
    ^ values

    "Modified: / 24-08-2017 / 23:35:38 / cg"
!

readShorts:nShorts signed:isSigned
    "read nShorts signed or unsigned short numbers (16bit) and return them in an array"

    |oldPos offset values msb val1 val2 val3 val4
     n "{ Class: SmallInteger }" |

    n := nShorts.

    msb := (byteOrder ~~ #lsb).
    values := Array basicNew:n.
    (isBigTiff and:[ (n <= 4) ]) ifTrue:[ 
        isSigned ifTrue:[
            val1 := inStream nextInt16MSB:msb.
            val2 := inStream nextInt16MSB:msb.
            val3 := inStream nextInt16MSB:msb.
            val4 := inStream nextInt16MSB:msb.
        ] ifFalse:[
            val1 := inStream nextUnsignedInt16MSB:msb.
            val2 := inStream nextUnsignedInt16MSB:msb.
            val3 := inStream nextUnsignedInt16MSB:msb.
            val4 := inStream nextUnsignedInt16MSB:msb.
        ].
        values at:1 put:val1.
        (n >= 2) ifTrue:[
            values at:2 put:val2.
            (n >= 3) ifTrue:[
                values at:3 put:val3.
                (n == 4) ifTrue:[
                    values at:4 put:val4.
                ]
            ]
        ]
    ] ifFalse:[
        (n <= 2) ifTrue:[
            isSigned ifTrue:[
                val1 := inStream nextInt16MSB:msb.
                val2 := inStream nextInt16MSB:msb.
            ] ifFalse:[
                val1 := inStream nextUnsignedInt16MSB:msb.
                val2 := inStream nextUnsignedInt16MSB:msb.
            ].
            values at:1 put:val1.
            (n == 2) ifTrue:[
                values at:2 put:val2
            ]
        ] ifFalse:[
            offset := inStream nextInt32MSB:msb.
            oldPos := inStream position.
            inStream position:offset.
            1 to:n do:[:index |
                isSigned ifTrue:[
                    val1 := inStream nextInt16MSB:msb.
                ] ifFalse:[
                    val1 := inStream nextUnsignedInt16MSB:msb.
                ].
                values at:index put:val1
            ].
            inStream position:oldPos
        ].
    ].
    ^ values

    "Modified: / 24-08-2017 / 23:33:09 / cg"
!

readSingleTagFrom:aStream
    |msb tagType numberType length|

    msb := (byteOrder == #msb).
    
    tagType := aStream nextUnsignedInt16MSB:msb.
    numberType := aStream nextUnsignedInt16MSB:msb.

    isBigTiff ifFalse:[
        length := aStream nextInt32MSB:msb.
    ] ifTrue:[
        length := aStream nextInt64MSB:msb.
    ].
    self decodeTiffTag:tagType numberType:numberType length:length.

    "Created: / 26-08-2017 / 11:04:50 / cg"
    "Modified (format): / 26-08-2017 / 22:37:58 / cg"
!

readTagsFrom:aStream
    |numberOfTags msb|

    msb := (byteOrder == #msb).
    
    isBigTiff ifFalse:[
        numberOfTags := aStream nextUnsignedInt16MSB:msb.
    ] ifTrue:[
        numberOfTags := aStream nextUnsignedInt64MSB:msb.
    ].
    1 to:numberOfTags do:[:index |
        self readSingleTagFrom:aStream.
    ].

    "Created: / 26-08-2017 / 11:03:45 / cg"
! !

!TIFFReader methodsFor:'private-writing'!

writeBitsPerSample
"
'bitsPerSample: ' print. bitsPerSample printNewline.
'store bitspersample at: ' print. outStream position printNewline.
"
    bitsPerSamplePos := outStream position.
    bitsPerSample do:[:n |
        self writeShort:n
    ]
!

writeColorMap
    |n|

    colorMapPos := outStream position.
    #(red green blue) do:[:component |
        n := 0.
        colorMap do:[:clr |
            |entry|

            clr isNil ifTrue:[
                entry := 0
            ] ifFalse:[
                entry := clr perform:component.
                "
                 tiff map is 16 bit - scale from percent to 0..16rFFFF
                "
                entry := (entry * 16rFFFF / 100) rounded.
            ].
            self writeShort:entry.
            n := n + 1
        ].

        "
         fill to 256 entries
        "
        [n < 256] whileTrue:[
            self writeShort:0.
            n := n + 1.
        ]
    ]

    "Modified: 20.2.1997 / 18:06:10 / cg"
!

writeStripByteCounts
"
'stripByteCounts: ' print. stripByteCounts printNewline.
'store stripbytecounts at: ' print. outStream position printNewline.
"
    stripByteCountsPos := outStream position.
    stripByteCounts do:[:c |
        self writeShort:c
    ]
!

writeStripOffsets
"
'stripOffsets: ' print. stripOffsets printNewline.
'store stripoffsets at: ' print. outStream position printNewline.
"
    stripOffsetsPos := outStream position.
    stripOffsets do:[:o |
        self writeLong:o
    ]
!

writeTag:tagType
    self writeTiffTag:tagType.
!

writeTiffTag:tagType
    |value valueArray numberType count address|

    count := 1.
    address := nil.
    (tagType == 253) ifTrue:[
        "tiff class"
    ].
    (tagType == 254) ifTrue:[
    ].
    (tagType == 255) ifTrue:[
        "SubfileType"
        value := subFileType.
        numberType := #long.
    ].
    (tagType == 256) ifTrue:[
        "ImageWidth"
        value := width.
        numberType := #short.
    ].
    (tagType == 257) ifTrue:[
        "ImageHeight"
        value := height.
        numberType := #short.
    ].
    (tagType == 258) ifTrue:[
        "bitspersample"
        address := bitsPerSamplePos.
        numberType := #short.
        count := bitsPerSample size.
        valueArray := bitsPerSample
    ].
    (tagType == 259) ifTrue:[
        "compression"
        value := compression.
        numberType := #short.
    ].
    (tagType == 262) ifTrue:[
        "photometric"
        (photometric == #whiteIs0) ifTrue:[
            value := 0
        ] ifFalse:[
            (photometric == #blackIs0) ifTrue:[
                value := 1
            ] ifFalse:[
                ((photometric == #rgb) or:[(photometric == #rgba)]) ifTrue:[
                    value := 2
                ] ifFalse:[
                    (photometric == #palette) ifTrue:[
                        value := 3
                    ] ifFalse:[
                        (photometric == #transparency) ifTrue:[
                            value := 4
                        ] ifFalse:[
                            self error:('TIFF-writer: unsupported photometric: ',photometric asString) mayProceed:true.
                        ]
                    ]
                ]
            ]
        ].
        numberType := #short.
    ].
    (tagType == 263) ifTrue:[
    ].
    (tagType == 264) ifTrue:[
    ].
    (tagType == 265) ifTrue:[
    ].
    (tagType == 266) ifTrue:[
        "fillOrder"
        (fillOrder == #msb) ifTrue:[
            value := FILLORDER_MSB2LSB
        ] ifFalse:[
            (fillOrder == #lsb) ifTrue:[
                value := FILLORDER_LSB2MSB
            ] ifFalse:[
                self error:'bad fillOrder' mayProceed:true
            ]
        ].
        numberType := #short.
    ].
    (tagType == 269) ifTrue:[
    ].
    (tagType == 270) ifTrue:[
    ].
    (tagType == 271) ifTrue:[
    ].
    (tagType == 272) ifTrue:[
    ].
    (tagType == 273) ifTrue:[
        "stripoffsets"
        address := stripOffsetsPos.
        numberType := #long.
        count := stripOffsets size.
        valueArray := stripOffsets
    ].
    (tagType == 274) ifTrue:[
    ].
    (tagType == 277) ifTrue:[
        "samplesPerPixel"
        value := samplesPerPixel.
        numberType := #short.
    ].
    (tagType == 278) ifTrue:[
        "rowsperstrip"
        value := rowsPerStrip.
        numberType := #short.
    ].
    (tagType == 279) ifTrue:[
        "stripbytecount"
        address := stripByteCountsPos.
        numberType := #short.
        count := stripByteCounts size.
        valueArray := stripByteCounts
    ].
    (tagType == 280) ifTrue:[
        "min sample value"
    ].
    (tagType == 281) ifTrue:[
        "max sample value"
    ].
    (tagType == 282) ifTrue:[
        "x resolution"
    ].
    (tagType == 283) ifTrue:[
        "y resolution"
    ].
    (tagType == 284) ifTrue:[
        "planarconfig"
        value := planarConfiguration.
        numberType := #short.
    ].
    (tagType == 285) ifTrue:[
        "pageName"
    ].
    (tagType == 286) ifTrue:[
        "xPosition"
    ].
    (tagType == 287) ifTrue:[
        "yPosition"
    ].
    (tagType == 288) ifTrue:[
        "freeOffsets"
    ].
    (tagType == 289) ifTrue:[
        "freeByteCounts"
    ].
    (tagType == 290) ifTrue:[
        "grayResponceUnit"
    ].
    (tagType == 291) ifTrue:[
        "grayResponceCurve"
    ].
    (tagType == 292) ifTrue:[
        "group3options"
        value := group3options.
        numberType := #long.
    ].
    (tagType == 293) ifTrue:[
        "group4options"
    ].
    (tagType == 296) ifTrue:[
        "resolutionunit"
        ^ self
    ].
    (tagType == 297) ifTrue:[
        "pageNumber"
    ].
    (tagType == 300) ifTrue:[
        "colorResponceUnit"
    ].
    (tagType == 301) ifTrue:[
        "colorResponceCurve"
    ].
    (tagType == 306) ifTrue:[
        "dateTime"
    ].
    (tagType == 315) ifTrue:[
        "artist"
    ].
    (tagType == 317) ifTrue:[
        "predictor"
    ].
    (tagType == 320) ifTrue:[
        "colormap"
        address := colorMapPos.
        numberType := #short.
        count := 256 "(colorMap at:1) size" * 3.
    ].

    (value isNil and:[address isNil]) ifTrue:[
        self error:'unhandled tag' mayProceed:true.
        ^ self
    ].

"
'tag:' print. tagType print. ' typ:' print. numberType print.
' len:' print. count print.
' val:' print. value printNewline.  
"

    self writeShort:tagType.
    numberType == #short ifTrue:[
        self writeShort:3.
        self writeLong:count.
    ] ifFalse:[
        numberType == #long ifTrue:[
            self writeShort:4.
            self writeLong:count.
        ] ifFalse:[
            numberType == #byte ifTrue:[
                self writeShort:1.
                self writeLong:count.
            ] ifFalse:[
                self error:'bad numbertype'
            ]
        ]
    ].
    address notNil ifTrue:[
        (numberType == #long and:[count == 1]) ifTrue:[
            self writeLong:(valueArray at:1).
            ^ self
        ].
        (numberType == #short and:[count <= 2]) ifTrue:[
            self writeShort:(valueArray at:1).
            count == 2 ifTrue:[
                self writeShort:(valueArray at:2).
            ] ifFalse:[
                self writeShort:0
            ].
            ^ self
        ].
        (numberType == #byte and:[count <= 4]) ifTrue:[
            outStream nextPut:(valueArray at:1).
            count > 1 ifTrue:[
                outStream nextPut:(valueArray at:2).
                count > 2 ifTrue:[
                    outStream nextPut:(valueArray at:3).
                    count > 3 ifTrue:[
                        outStream nextPut:(valueArray at:4).
                    ] ifFalse:[
                        outStream nextPut:0
                    ].
                ] ifFalse:[
                    outStream nextPut:0
                ].
            ] ifFalse:[
                outStream nextPut:0
            ].
            ^ self
        ].
        self writeLong:address.
        ^ self
    ].
    numberType == #short ifTrue:[
        self writeShort:value.
        self writeShort:0
    ] ifFalse:[
        numberType == #long ifTrue:[
            self writeLong:value
        ] ifFalse:[
            numberType == #byte ifTrue:[
                outStream nextPut:value.
                outStream nextPut:0.
                outStream nextPut:0.
                outStream nextPut:0.
            ] ifFalse:[
                self error:'bad numbertype'
            ]
        ]
    ].

    "Modified: / 13-09-2017 / 10:08:52 / cg"
!

writeUncompressedBits
    "write bits as one or multiple strips"

    |offs bytesPerRow nBytes
     h "{ Class: SmallInteger }"|

    nBytes := data size.
    nBytes < 16rFFFF ifTrue:[
        stripOffsets := Array with:(outStream position).
        stripByteCounts := Array with:nBytes.
        outStream nextPutBytes:nBytes from:data.
        rowsPerStrip := height
    ] ifFalse:[
        stripOffsets := Array basicNew:height.
        bytesPerRow := nBytes // height.
        stripByteCounts := (Array basicNew:height) atAllPut:bytesPerRow.

        offs := 1.
        h := height.
        1 to:h do:[:row |
            stripOffsets at:row put:(outStream position).
            outStream nextPutBytes:bytesPerRow from:data startingAt:offs.
            offs := offs + bytesPerRow
        ].
        rowsPerStrip := 1
    ].
"
    'stripOffsets: ' print. stripOffsets printNewline.
    'stripByteCounts: ' print. stripByteCounts printNewline.
"
! !

!TIFFReader methodsFor:'reading'!

fromStream:aStream
    "read a stream containing a TIFF image.
     Leave image description in instance variables.
     (i.e. to get the image, ask with image)."

    |char1 char2 version offset msb
     bytesPerRow img moreIfds|

    inStream := aStream.
    aStream binary.

    char1 := aStream next.
    char2 := aStream next.
    
    "/ first two chars are either II (intel byte order) 
    "/ or MM (motorola byte orrder)
    (char1 ~~ char2) ifTrue:[
        ^ self fileFormatError:'not a tiff file'.
    ].
    (char1 == $I codePoint) ifTrue:[
        byteOrder := #lsb.
        msb := false.
    ] ifFalse:[
        (char1 == $M codePoint) ifTrue:[
            byteOrder := #msb.
            msb := true.
        ] ifFalse:[
            ^ self fileFormatError:'not a tiff file'.
        ]
    ].
    
    version := aStream nextUnsignedInt16MSB:msb.
    (version == 42) ifTrue:[
        isBigTiff := false.
    ] ifFalse:[
       (version == 43) ifTrue:[
            |byteSizeOfOffsets always0|

            "/ 43 is the proposed bigtiff format
            isBigTiff := true.
            byteSizeOfOffsets := aStream nextUnsignedInt16MSB:msb.
            byteSizeOfOffsets == 8 ifFalse:[
                ^ self fileFormatError:'version of bigtiff-file not supported'.
            ].
            always0 := aStream nextUnsignedInt16MSB:msb.
            always0 == 0 ifFalse:[
                ^ self fileFormatError:'version of bigtiff-file not supported'.
            ].
        ] ifFalse:[    
            ^ self fileFormatError:'version of tiff-file not supported'.
        ].
    ].

    imageSequence := ImageSequence new.
    isDNGImage := false.

    isBigTiff ifFalse:[
        offset := aStream nextUnsignedInt32MSB:msb.
    ] ifTrue:[
        offset := aStream nextUnsignedInt64MSB:msb.
    ].    

    [ offset ~~ 0 ] whileTrue:[    
        Verbose == true ifTrue:[
            Logger info:'------------------ reading image #%1 ------------------' with:imageSequence size+1.
        ].    
        
        aStream position:offset.

        "setup default values"
        metaData := TIFFMetaData new.

        compression := 1. "none"
        fillOrder := #msb.
        planarConfiguration := PLANARCONFIG_CONTIG.
        photometric := nil.
        bitsPerSample := nil.
        samplesPerPixel := 1.
        width := height := nil.
        stripOffsets := stripByteCounts := rowsPerStrip := nil.
        "resolutionUnit := 2."
        predictor := 1.
        group3options := nil.
        orientation := nil.
        subFileType := subIfds := nil.
        sampleFormat := SAMPLEFORMAT_UINT.
        minSampleValue := maxSampleValue := nil.
        tileWidth := tileLength := tileOffsets := tileByteCounts := nil.
        
        self readTagsFrom:aStream.
        "/ read the next offset now,
        "/ because the stream's position will be changed when the image-data is read
        isBigTiff ifFalse:[
            offset := aStream nextUnsignedInt32MSB:msb.
        ] ifTrue:[
            offset := aStream nextUnsignedInt64MSB:msb.
        ].    

        "check for required tags"
        width isNil ifTrue:[ ^ self fileFormatError:'missing width tag' ].
        height isNil ifTrue:[ ^ self fileFormatError:'missing length tag' ].
        bitsPerSample isNil ifTrue:[
            bitsPerSample := Array new:samplesPerPixel withAll:1.
        ].
        
        photometric isNil ifTrue:[
            "/ try to repair
            bitsPerSample sum == 1 ifTrue:[
                photometric := #whiteIs0.
            ] ifFalse:[    
                ^ self fileFormatError:'missing photometric tag' 
            ].
        ].

        "given all the information, read the bits"
        tileWidth notNil ifTrue:[
            tileOffsets := tileOffsets ? stripOffsets.
            tileByteCounts := tileByteCounts ? stripByteCounts.
            (tileOffsets notNil 
                and:[tileLength notNil
                and:[tileByteCounts notNil ]]
            ) ifFalse:[    
                ^ self fileFormatError:'missing stripOffsets (or tileOffsets)'.
            ].
            self reportDimension.
            self readTiledTiffImageData.
        ] ifFalse:[
            stripByteCounts isNil ifTrue:[
                stripOffsets size == 1 ifTrue:[
                    stripByteCounts := Array with:(self bitsPerPixel // 8) * width * height
                ] ifFalse:[
                    ^ self fileFormatError:'missing stripByteCounts'.
                ].    
            ].
            rowsPerStrip isNil ifTrue:[
                rowsPerStrip := height
            ].
            self reportDimension.
            self readTiffImageData.
        ].
    
        orientation notNil ifTrue:[
            orientation == #unsupported ifTrue:[
                'TIFFReader [warning]: unsupported orientation' errorPrintCR
            ] ifFalse:[
                orientation == #vFlip ifTrue:[
                    |tmp offset1 offset2|

                    "/ reverse rows to top-to bottom 
                    "/ (oops - depends on side-effecting the data array)

                    bytesPerRow := self bytesPerRow.
                    tmp := ByteArray new:bytesPerRow.
                    offset1 := 1.
                    offset2 := (height-1)*bytesPerRow + 1.
                    0 to:((height-1)//2) do:[:row |
                        tmp replaceFrom:1 to:bytesPerRow with:data startingAt:offset1.
                        data replaceFrom:offset1 to:(offset1+bytesPerRow-1) with:data startingAt:offset2.
                        data replaceFrom:offset2 to:(offset2+bytesPerRow-1) with:tmp startingAt:1.
                        offset1 := offset1 + bytesPerRow.
                        offset2 := offset2 - bytesPerRow.
                    ].
                ].
            ].
        ].
        
        "/ fixup photoetric
        photometric == #rgb ifTrue:[
            samplesPerPixel == 4 ifTrue:[
                photometric := #rgba
            ].    
        ].
        
        img := self makeImage.
        img imageSequence:imageSequence.
        imageSequence add:(ImageFrame new image:img).

        offset == 0 ifTrue:[
            moreIfds isNil ifTrue:[
                subIfds notNil ifTrue:[
                    (isDNGImage not or:[loadFullResolutionImage == true]) ifTrue:[
                        moreIfds := OrderedCollection withAll:subIfds.
                        subIfds := nil.
                    ] ifFalse:[
                        Verbose == true ifTrue:[
                            Logger info:'skip high resolution images (use loadFullResolutionImage is false)'
                        ].    
                    ].    
                ].
            ].
            moreIfds notEmptyOrNil ifTrue:[
                offset := moreIfds removeFirst
            ].    
        ].    
    ].

    imageSequence size > 1 ifTrue:[
        Verbose == true ifTrue:[
            Logger info:'read %1 images' with:imageSequence size
        ].    
    ].    
    ^ self

    "Modified: / 27-08-2017 / 23:24:37 / cg"
! !

!TIFFReader methodsFor:'writing'!

save:image onStream:aStream
    "save image as (uncompressed) TIFF file on aFileName"

    |pos1 pos indicator|

    image mask notNil ifTrue:[
        Image informationLostQuerySignal
            raiseWith:image
            errorString:('TIFF writer does not (yet) support an imageMask').
    ].

    outStream := aStream.
    outStream binary.

    "save as msb"

    byteOrder := #msb.
"
    byteOrder := #lsb.
"
    fillOrder := #msb.
    width := image width.
    height := image height.
    photometric := image photometric.
    samplesPerPixel := image samplesPerPixel.
    bitsPerSample := image bitsPerSample.

    colorMap := image colorMap.
    planarConfiguration := PLANARCONFIG_CONTIG.
    compression := 1.   "none"
    data := image bits.

    currentOffset := 0.

    (byteOrder == #msb) ifTrue:[
        indicator := $M codePoint.
    ] ifFalse:[
        indicator := $I codePoint.
    ].
    outStream nextPut:indicator; nextPut:indicator.
    currentOffset := currentOffset + 2.

    self writeShort:42.
    currentOffset := currentOffset + 2.

    pos1 := outStream position.
    self writeLong:0.           "start of tags - filled in later"
    currentOffset := currentOffset + 4.

    "output strips"

    self writeUncompressedBits. "this outputs bits as strips, sets stripOffsets and stripByteCounts"
    self writeStripOffsets.     "this outputs strip offsets, sets stripOffsetsPos"
    self writeStripByteCounts.  "this outputs strip bytecounts, sets stripByteCountPos"
    self writeBitsPerSample.    "this outputs bitsPerSample, sets bitsPerSamplePos"
    photometric == #palette ifTrue:[
        self writeColorMap      "this outputs colorMap, sets colorMapPos"
    ].

    pos := outStream position.        "backpatch tag offset"
    outStream position:pos1.
    self writeLong:pos.                     "fill in tag offset"
    outStream position:pos.
"
('patch tag offset at: ', (pos1 printStringRadix:16) , ' to ',
                         (pos printStringRadix:16)) printNewline.
"
    "output tag data"

    photometric == #palette ifTrue:[
        self writeShort:11.  "11 tags"
    ] ifFalse:[
        self writeShort:10.  "10 tags"
    ].
    self writeTag:256.               "image width"
    self writeTag:257.               "image height"
    self writeTag:258.               "bits per sample"
    self writeTag:259.               "compression"
    self writeTag:262.               "photometric"
    self writeTag:273.               "strip offsets"
    self writeTag:277.               "samplesPerPixel"
    self writeTag:278.               "rowsPerStrip"
    self writeTag:279.               "strip byte counts"
    self writeTag:284.               "planarconfig"
    photometric == #palette ifTrue:[
        self writeTag:320            "colorMap"
    ].
    self writeLong:0.                "end of tags mark"

    "Modified: / 27-08-2017 / 13:36:35 / cg"
! !

!TIFFReader class methodsFor:'documentation'!

version
    ^ '$Header$'
!

version_CVS
    ^ '$Header$'
! !


TIFFReader initialize!