    /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
    /*
     * This file is part of the LibreOffice project.
     *
     * This Source Code Form is subject to the terms of the Mozilla Public
     * License, v. 2.0. If a copy of the MPL was not distributed with this
     * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     *
     * This file incorporates work covered by the following license notice:
     *
     *   Licensed to the Apache Software Foundation (ASF) under one or more
     *   contributor license agreements. See the NOTICE file distributed
     *   with this work for additional information regarding copyright
     *   ownership. The ASF licenses this file to you under the Apache
     *   License, Version 2.0 (the "License"); you may not use this file
     *   except in compliance with the License. You may obtain a copy of
     *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
     */

    #include <memory>
    #include <sal/config.h>
    #include <sal/log.hxx>

    #include <vcl/FilterConfigItem.hxx>
    #include <vcl/graph.hxx>
    #include <vcl/BitmapTools.hxx>
    #include <vcl/animate.hxx>
    #include <tools/fract.hxx>
    #include "lzwdecom.hxx"
    #include "ccidecom.hxx"

    namespace {

    template< typename T > T BYTESWAP(T nByte) {
        return ( nByte << 7 ) | ( ( nByte & 2 ) << 5 ) | ( ( nByte & 4 ) << 3 ) |
            ( ( nByte & 8 ) << 1 ) | ( ( nByte & 16 ) >> 1 ) |
            ( ( nByte & 32 ) >> 3 ) | ( ( nByte & 64 ) >> 5 ) |
            ( ( nByte & 128 ) >> 7 );
    }

    }

    //============================ TIFFReader ==================================

    class TIFFReader
    {

    private:

        bool                bStatus;                    // Whether until now no error occurred
        Animation               aAnimation;

        SvStream*               pTIFF;                      // the TIFF file that should be read
        std::vector<sal_uInt8>  maBitmap;
        Size                    maBitmapPixelSize;
        std::vector<Color>      mvPalette;
        MapMode                 maBitmapPrefMapMode;
        Size                    maBitmapPrefSize;
        sal_uInt16              nDstBitsPerPixel;
        int                     nLargestPixelIndex;

        sal_uInt64              nOrigPos;                   // start position in pTIFF
        sal_uInt64              nEndOfFile;                 // end of file position in pTIFF

        sal_uInt16              nDataType;
        // Data taken from the TIFF tags:
        bool                    bByteSwap;                  // sal_True if bits 0..7 -> 7..0 should get converted ( FILLORDER = 2 );

        sal_uInt32              nNewSubFile;
        sal_uInt32              nSubFile;
        sal_Int32               nImageWidth;                // picture width in pixels
        sal_Int32               nImageLength;               // picture height in pixels
        sal_uInt32              nBitsPerSample;             // bits per pixel per layer
        sal_uInt32              nCompression;               // kind of compression
        sal_uInt32              nPhotometricInterpretation;
        sal_uInt32              nThresholding;
        sal_uInt32              nCellWidth;
        sal_uInt32              nCellLength;
        sal_uInt32              nFillOrder;
        std::vector<sal_uInt64> aStripOffsets;              // field of offsets to the Bitmap-Data-"Strips"
        sal_uInt32              nOrientation;
        sal_uInt32              nSamplesPerPixel;           // number of layers
        sal_uInt32              nRowsPerStrip;              // if it's not compressed: number of rows per Strip
        std::vector<sal_uInt32> aStripByteCounts;           // if compressed (in a certain way): size of the strips
        sal_uInt32              nMinSampleValue;
        sal_uInt32              nMaxSampleValue;
        double                  fXResolution;               // X-resolution or 0.0
        double                  fYResolution;               // Y-resolution or 0.0
        sal_uInt32              nPlanarConfiguration;
        sal_uInt32              nGroup3Options;
        sal_uInt32              nGroup4Options;
        sal_uInt32              nResolutionUnit;            // unit of fX/YResolution: 1=unknown, 2(default)=inch, 3=cm
        sal_uInt32              nPredictor;
        std::vector<sal_uInt32> aColorMap;                  // color palette
        sal_uInt32              nNumColors;                 // number of colors within the color palette

        sal_uInt32              nPlanes;                    // number of layers within the Tiff file
        sal_uInt32              nStripsPerPlane;            // number of Strips per layer
        sal_uInt32              nBytesPerRow;               // Bytes per line per Layer in the Tiff file ( uncompressed )
        std::vector<sal_uInt8>  aMap[4];                    // temporary Scanline

        sal_uInt32 DataTypeSize();
        sal_uInt32 ReadIntData();
        double  ReadDoubleData();

        void    ReadHeader();
        void    ReadTagData( sal_uInt16 nTagType, sal_uInt32 nDataLen );

        sal_uInt8* getMapData(sal_uInt32 np);

        bool    ReadMap();
            // reads/decompresses the bitmap data and fills aMap

        sal_uInt32 GetBits(const sal_uInt8 * pSrc, sal_uInt32 nBitsPos, sal_uInt32 nBitsCount);
            // fetches BitsCount bits from pSrc[..] at the position nBitsPos

        void    MakePalCol();
            // Create the bitmap from the temporary bitmap aMap
            // and partly deletes aMap while doing this.

        bool    ConvertScanline(sal_Int32 nY);
            // converts a Scanline to the Windows-BMP format

        bool HasAlphaChannel() const;

        void SetPixel(long nY, long nX, sal_uInt8 cIndex);
        void SetPixel(long nY, long nX, Color c);
        void SetPixelAlpha(long nY, long nX, sal_uInt8 nAlpha);

    public:

        TIFFReader()
            : bStatus(false)
            , pTIFF(nullptr)
            , nDstBitsPerPixel(0)
            , nLargestPixelIndex(-1)
            , nOrigPos(0)
            , nEndOfFile(0)
            , nDataType(0)
            , bByteSwap(false)
            , nNewSubFile(0)
            , nSubFile(0)
            , nImageWidth(0)
            , nImageLength(0)
            , nBitsPerSample(1)
            , nCompression(1)
            , nPhotometricInterpretation(0)
            , nThresholding(1)
            , nCellWidth(1)
            , nCellLength(1)
            , nFillOrder(1)
            , nOrientation(1)
            , nSamplesPerPixel(1)
            , nRowsPerStrip(0xffffffff)
            , nMinSampleValue(0)
            , nMaxSampleValue(0)
            , fXResolution(0.0)
            , fYResolution(0.0)
            , nPlanarConfiguration(1)
            , nGroup3Options(0)
            , nGroup4Options(0)
            , nResolutionUnit(2)
            , nPredictor(0)
            , nNumColors(0)
            , nPlanes(0)
            , nStripsPerPlane(0)
            , nBytesPerRow(0)
        {
        }

        sal_uInt32 GetRowsPerStrip() const
        {
            //Rows Per Strip:
            //
            //(TIFF format only) The number of rows of pixels per strip to use for
            //encoding the TIFF image. A value greater than zero specifies the
            //number of rows per strip. A value of 0 sets the rows per strip equal
            //to the image length, resulting in a single strip. A value of -1 (the
            //default) sets the rows per strip equal to infinity, resulting in a
            //single strip.
            return nRowsPerStrip == 0 ? nImageLength : nRowsPerStrip;
        }

        bool ReadTIFF( SvStream & rTIFF, Graphic & rGraphic );
    };

    //=================== Methods of TIFFReader ==============================

    sal_uInt32 TIFFReader::DataTypeSize()
    {
        sal_uInt32 nSize;
        switch ( nDataType )
        {
            case 1 :            // BYTE
            case 2 :            // ASCII
            case 6 :            // SIGNED Byte
            case 7 :            // UNDEFINED
                nSize = 1;
                break;
            case 3 :            // UINT16
            case 8 :            // INT16
                nSize = 2;
                break;
            case 4 :            // UINT32
            case 9 :            // INT32
            case 11 :           // FLOAT
                nSize = 4;
                break;
            case 5 :            // RATIONAL
            case 10 :           // SIGNED RATIONAL
            case 12 :           // DOUBLE
                nSize = 8;
                break;
            default:
                pTIFF->SetError(SVSTREAM_FILEFORMAT_ERROR);
                nSize=1;
        }
        return nSize;
    }

    sal_uInt32 TIFFReader::ReadIntData()
    {
        double  nDOUBLE(0.0);
        float   nFLOAT(0);
        sal_uInt32  nUINT32a(0), nUINT32b(0);
        sal_Int32   nINT32(0);
        sal_uInt16  nUINT16(0);
        sal_Int16   nINT16(0);
        sal_uInt8   nBYTE(0);
        char    nCHAR(0);

        switch( nDataType )
        {
            case 0 :    //??
            case 1 :
            case 2 :
            case 7 :
                pTIFF->ReadUChar( nBYTE );
                nUINT32a = nBYTE;
            break;
            case 3 :
                 pTIFF->ReadUInt16( nUINT16 );
                 nUINT32a = nUINT16;
            break;
            case 9 :
            case 4 :
                pTIFF->ReadUInt32( nUINT32a );
            break;
            case  5 :
                pTIFF->ReadUInt32( nUINT32a ).ReadUInt32( nUINT32b );
                if ( nUINT32b != 0 )
                    nUINT32a /= nUINT32b;
            break;
            case 6 :
                pTIFF->ReadChar( nCHAR );
                nUINT32a = static_cast<sal_Int32>(nCHAR);
            break;
            case 8 :
                pTIFF->ReadInt16( nINT16 );
                nUINT32a = static_cast<sal_Int32>(nINT16);
            break;
            case 10 :
                pTIFF->ReadUInt32( nUINT32a ).ReadInt32( nINT32 );
                if ( nINT32 != 0 )
                    nUINT32a /= nINT32;
            break;
            case 11 :
                pTIFF->ReadFloat( nFLOAT );
                if (!rtl::math::isNan(nFLOAT) && nFLOAT > SAL_MIN_INT32 - 1.0
                    && nFLOAT < SAL_MAX_INT32 + 1.0)
                {
                    nUINT32a = static_cast<sal_Int32>(nFLOAT);
                }
                else
                {
                    SAL_INFO("filter.tiff", "float " << nFLOAT << " outsider of sal_Int32 range");
                }
            break;
            case 12 :
                pTIFF->ReadDouble( nDOUBLE );
                if (!rtl::math::isNan(nDOUBLE) && nDOUBLE > SAL_MIN_INT32 - 1.0
                    && nDOUBLE < SAL_MAX_INT32 + 1.0)
                {
                    nUINT32a = static_cast<sal_Int32>(nDOUBLE);
                }
                else
                {
                    SAL_INFO("filter.tiff", "double " << nDOUBLE << " outsider of sal_Int32 range");
                }
            break;
            default:
                pTIFF->ReadUInt32( nUINT32a );
            break;
        }
        return nUINT32a;
    }

    double TIFFReader::ReadDoubleData()
    {
        switch (nDataType) {
        case 5:
            {
                sal_uInt32 nulong(0);
                pTIFF->ReadUInt32( nulong );
                double nd = static_cast<double>(nulong);
                nulong = 0;
                pTIFF->ReadUInt32( nulong );
                if ( nulong != 0 )
                    nd /= static_cast<double>(nulong);
                return nd;
            }

        case 11:
            {
                float x = 0;
                pTIFF->ReadFloat(x);
                return x;
            }

        case 12:
            {
                double x = 0;
                pTIFF->ReadDouble(x);
                return x;
            }

        default:
            return static_cast<double>(ReadIntData());
        }
    }

    void TIFFReader::ReadTagData( sal_uInt16 nTagType, sal_uInt32 nDataLen)
    {
        if ( !bStatus )
            return;

        switch ( nTagType )
        {
            case 0x00fe:   // New Sub File
                nNewSubFile = ReadIntData();
                SAL_INFO("filter.tiff","NewSubFile: " << nNewSubFile);
                break;

            case 0x00ff:   // Sub File
                nSubFile = ReadIntData();
                SAL_INFO("filter.tiff","SubFile: " << nSubFile);
                break;

            case 0x0100:   // Image Width
                nImageWidth = ReadIntData();
                SAL_INFO("filter.tiff","ImageWidth: " << nImageWidth);
                break;

            case 0x0101:   // Image Length
                nImageLength = ReadIntData();
                SAL_INFO("filter.tiff","ImageLength: " << nImageLength);
                break;

            case 0x0102:   // Bits Per Sample
                nBitsPerSample = ReadIntData();
                SAL_INFO("filter.tiff","BitsPerSample: " << nBitsPerSample);
                if ( nBitsPerSample >= 32 ) // 32 bit and larger samples are not supported
                    bStatus = false;
                break;

            case 0x0103:   // Compression
                nCompression = ReadIntData();
                SAL_INFO("filter.tiff","Compression: " << nCompression);
                break;

            case 0x0106:   // Photometric Interpretation
                nPhotometricInterpretation = ReadIntData();
                SAL_INFO("filter.tiff","PhotometricInterpretation: " << nPhotometricInterpretation);
                break;

            case 0x0107:   // Thresholding
                nThresholding = ReadIntData();
                SAL_INFO("filter.tiff","Thresholding: " << nThresholding);
                break;

            case 0x0108:   // Cell Width
                nCellWidth = ReadIntData();
                break;

            case 0x0109:   // Cell Length
                nCellLength = ReadIntData();
                break;

            case 0x010a:   // Fill Order
                nFillOrder = ReadIntData();
                SAL_INFO("filter.tiff","FillOrder: " << nFillOrder);
                break;

            case 0x0111: { // Strip Offset(s)
                size_t nOldNumSO = aStripOffsets.size();
                nDataLen += nOldNumSO;
                size_t const nMaxAllocAllowed = SAL_MAX_UINT32 / sizeof(sal_uInt32);
                size_t nMaxRecordsAvailable = pTIFF->remainingSize() / DataTypeSize();
                if (nDataLen > nOldNumSO && nDataLen < nMaxAllocAllowed &&
                    (nDataLen - nOldNumSO) <= nMaxRecordsAvailable)
                {
                    try
                    {
                        aStripOffsets.resize(nDataLen);
                        for (size_t i = 0; i < nOldNumSO; ++i)
                            aStripOffsets[i] += nOrigPos;
                        for (size_t i = nOldNumSO; i < aStripOffsets.size(); ++i)
                            aStripOffsets[i] = ReadIntData() + nOrigPos;
                    }
                    catch (const std::bad_alloc &)
                    {
                        aStripOffsets.clear();
                    }
                }
                SAL_INFO("filter.tiff","StripOffsets (Number:) " << nDataLen);
                break;
            }
            case 0x0112:   // Orientation
                nOrientation = ReadIntData();
                SAL_INFO("filter.tiff","Orientation: " << nOrientation);
                break;

            case 0x0115:   // Samples Per Pixel
                nSamplesPerPixel = ReadIntData();
                SAL_INFO("filter.tiff","SamplesPerPixel: " << nSamplesPerPixel);
                break;

            case 0x0116:   // Rows Per Strip
                nRowsPerStrip = ReadIntData();
                SAL_INFO("filter.tiff","RowsPerStrip: " << nRowsPerStrip);
                break;

            case 0x0117: { // Strip Byte Counts
                size_t nOldNumSBC = aStripByteCounts.size();
                nDataLen += nOldNumSBC;
                size_t const nMaxAllocAllowed = SAL_MAX_UINT32 / sizeof(sal_uInt32);
                size_t nMaxRecordsAvailable = pTIFF->remainingSize() / DataTypeSize();
                if (nDataLen > nOldNumSBC && nDataLen < nMaxAllocAllowed &&
                    (nDataLen - nOldNumSBC) <= nMaxRecordsAvailable)
                {
                    try
                    {
                        aStripByteCounts.resize(nDataLen);
                        for (size_t i = nOldNumSBC; i < aStripByteCounts.size(); ++i)
                            aStripByteCounts[i] = ReadIntData();
                    }
                    catch (const std::bad_alloc &)
                    {
                        aStripByteCounts.clear();
                    }
                }
                SAL_INFO("filter.tiff","StripByteCounts (Number:) " << nDataLen);
                break;
            }
            case 0x0118:   // Min Sample Value
                nMinSampleValue = ReadIntData();
                SAL_INFO("filter.tiff","MinSampleValue: " << nMinSampleValue);
                break;

            case 0x0119:   // Max Sample Value
                nMaxSampleValue = ReadIntData();
                SAL_INFO("filter.tiff","MaxSampleValue: " << nMaxSampleValue);
                break;

            case 0x011a:   // X Resolution
                fXResolution = ReadDoubleData();
                break;

            case 0x011b:   // Y Resolution
                fYResolution = ReadDoubleData();
                break;

            case 0x011c:   // Planar Configuration
                nPlanarConfiguration = ReadIntData();
                SAL_INFO("filter.tiff","PlanarConfiguration: " << nPlanarConfiguration);
                break;

            case 0x0124:   // Group 3 Options
                nGroup3Options = ReadIntData();
                SAL_INFO("filter.tiff","Group3Options: " << nGroup3Options);
                break;

            case 0x0125:   // Group 4 Options
                nGroup4Options = ReadIntData();
                SAL_INFO("filter.tiff","Group4Options: " << nGroup4Options);
                break;

            case 0x0128:   // Resolution Unit
                nResolutionUnit = ReadIntData();
                break;

            case 0x013d:   // Predictor
                nPredictor = ReadIntData();
                SAL_INFO("filter.tiff","Predictor: " << nPredictor);
                break;

            case 0x0140: { // Color Map
                sal_uInt16 nVal;
                nNumColors = (sal_uInt32(1) << nBitsPerSample);
                if ( nDataType == 3 && nNumColors <= 256)
                {
                    aColorMap.resize(256);
                    for (sal_uInt32 i = 0; i < nNumColors; ++i)
                        aColorMap[i] = 0;
                    for (sal_uInt32 i = 0; i < nNumColors; ++i)
                    {
                        pTIFF->ReadUInt16( nVal );
                        aColorMap[i] |= ( static_cast<sal_uInt32>(nVal) << 8 ) & 0x00ff0000;
                    }
                    for (sal_uInt32 i = 0; i < nNumColors; ++i)
                    {
                        pTIFF->ReadUInt16( nVal );
                        aColorMap[i] |= static_cast<sal_uInt32>(nVal) & 0x0000ff00;
                    }
                    for (sal_uInt32 i = 0; i < nNumColors; ++i)
                    {
                        pTIFF->ReadUInt16( nVal );
                        aColorMap[i] |= ( static_cast<sal_uInt32>(nVal) >> 8 ) & 0x000000ff;
                    }
                }
                else
                    bStatus = false;
                SAL_INFO("filter.tiff","ColorMap (number of colors): " << nNumColors);
                break;
            }

            case 0x0153: { // SampleFormat
                sal_uInt32 nSampleFormat = ReadIntData();
                if ( nSampleFormat == 3 ) // IEEE floating point samples are not supported yet
                    bStatus = false;
                break;
            }
        }

        if ( pTIFF->GetError() )
            bStatus = false;
    }

    sal_uInt8* TIFFReader::getMapData(sal_uInt32 np)
    {
        aMap[np].resize(nBytesPerRow);
        return aMap[np].data();
    }

    bool TIFFReader::ReadMap()
    {
        //when fuzzing with a max len set, max decompress to 2000 times that limit
        static size_t nMaxAllowedDecompression = [](const char* pEnv) { size_t nRet = pEnv ? std::atoi(pEnv) : 0; return nRet * 2000; }(std::getenv("FUZZ_MAX_INPUT_LEN"));
        size_t nTotalDataRead = 0;

        if ( nCompression == 1 || nCompression == 32771 )
        {
            sal_uInt32 nStripBytesPerRow;

            if ( nCompression == 1 )
                nStripBytesPerRow = nBytesPerRow;
            else
                nStripBytesPerRow = ( nBytesPerRow + 1 ) & 0xfffffffe;
            for (sal_Int32 ny = 0; ny < nImageLength; ++ny)
            {
                for (sal_uInt32 np = 0; np < nPlanes; ++np)
                {
                    if (np >= SAL_N_ELEMENTS(aMap))
                        return false;
                    sal_uInt32 nStrip = ny / GetRowsPerStrip() + np * nStripsPerPlane;
                    if ( nStrip >= aStripOffsets.size())
                        return false;
                    pTIFF->Seek( aStripOffsets[ nStrip ] + ( ny % GetRowsPerStrip() ) * nStripBytesPerRow );
                    pTIFF->ReadBytes(getMapData(np), nBytesPerRow);
                    if (!pTIFF->good())
                        return false;
                }
                if ( !ConvertScanline( ny ) )
                    return false;
            }
        }
        else if ( nCompression == 2 || nCompression == 3 || nCompression == 4 )
        {
            sal_uInt32 nOptions;
            if ( nCompression == 2 )
            {
                nOptions = CCI_OPTION_BYTEALIGNROW;
            }
            else if ( nCompression == 3 )
            {
                nOptions = CCI_OPTION_EOL;
                if ( nGroup3Options & 0x00000001 )
                    nOptions |= CCI_OPTION_2D;
                if ( nGroup3Options & 0x00000004 )
                    nOptions |= CCI_OPTION_BYTEALIGNEOL;
                if ( nGroup3Options & 0xfffffffa )
                    return false;
            }
            else
            {   // nCompression==4
                nOptions = CCI_OPTION_2D;
                if ( nGroup4Options & 0xffffffff )
                    return false;
            }
            if ( nFillOrder == 2 )
            {
                nOptions |= CCI_OPTION_INVERSEBITORDER;
                bByteSwap = false;
            }
            sal_uInt32 nStrip = 0;
            if (nStrip >= aStripOffsets.size())
                return false;
            sal_uInt64 nOffset = aStripOffsets[nStrip];
            if (nOffset > nEndOfFile)
                return false;
            pTIFF->Seek(aStripOffsets[nStrip]);

            CCIDecompressor aCCIDecom( nOptions, nImageWidth );

            aCCIDecom.StartDecompression( *pTIFF );

            const bool bHasAlphaChannel = HasAlphaChannel();
            for (sal_Int32 ny = 0; ny < nImageLength; ++ny)
            {
                bool bDifferentToPrev = ny == 0;
                for (sal_uInt32 np = 0; np < nPlanes; ++np)
                {
                    if ( ny / GetRowsPerStrip() + np * nStripsPerPlane > nStrip )
                    {
                        nStrip=ny/GetRowsPerStrip()+np*nStripsPerPlane;
                        if (nStrip >= aStripOffsets.size())
                            return false;
                        nOffset = aStripOffsets[nStrip];
                        if (nOffset > nEndOfFile)
                            return false;
                        pTIFF->Seek(nOffset);
                        aCCIDecom.StartDecompression( *pTIFF );
                    }
                    if (np >= SAL_N_ELEMENTS(aMap))
                        return false;
                    DecompressStatus aResult = aCCIDecom.DecompressScanline(getMapData(np), nImageWidth * nBitsPerSample * nSamplesPerPixel / nPlanes, np + 1 == nPlanes);
                    if (!aResult.m_bSuccess)
                        return false;
                    bDifferentToPrev |= !aResult.m_bBufferUnchanged;
                    if ( pTIFF->GetError() )
                        return false;
                    nTotalDataRead += nBytesPerRow;
                    if (nMaxAllowedDecompression && nTotalDataRead > nMaxAllowedDecompression)
                        return false;
                }
                if (!bDifferentToPrev)
                {
                    //if the buffer for this line didn't change, then just copy the
                    //previous scanline instead of painfully decoding and setting
                    //each pixel one by one again
                    const int nColorSize = bHasAlphaChannel ? 4 : 3;
                    memcpy( maBitmap.data() + (ny * maBitmapPixelSize.Width()) * nColorSize,
                            maBitmap.data() + ((ny-1) * maBitmapPixelSize.Width()) * nColorSize,
                            maBitmapPixelSize.Width() * nColorSize);
                }
                else
                {
                    if (!ConvertScanline(ny))
                        return false;
                }
            }
        }
        else if ( nCompression == 5 )
        {
            LZWDecompressor aLZWDecom;
            sal_uInt32 nStrip(0);
            if (nStrip >= aStripOffsets.size())
                return false;
            pTIFF->Seek(aStripOffsets[nStrip]);
            aLZWDecom.StartDecompression(*pTIFF);
            for (sal_Int32 ny = 0; ny < nImageLength; ++ny)
            {
                for (sal_uInt32 np = 0; np < nPlanes; ++np)
                {
                    if ( ny / GetRowsPerStrip() + np * nStripsPerPlane > nStrip )
                    {
                        nStrip = ny / GetRowsPerStrip() + np * nStripsPerPlane;
                        if (nStrip >= aStripOffsets.size())
                            return false;
                        pTIFF->Seek(aStripOffsets[nStrip]);
                        aLZWDecom.StartDecompression(*pTIFF);
                    }
                    if (np >= SAL_N_ELEMENTS(aMap))
                        return false;
                    if ( ( aLZWDecom.Decompress(getMapData(np), nBytesPerRow) != nBytesPerRow ) || pTIFF->GetError() )
                        return false;
                }

                nTotalDataRead += nBytesPerRow;
                if (nMaxAllowedDecompression && nTotalDataRead > nMaxAllowedDecompression)
                    return false;

                if ( !ConvertScanline( ny ) )
                    return false;
            }
        }
        else if ( nCompression == 32773 )
        {
            sal_uInt32 nStrip(0);
            if (nStrip >= aStripOffsets.size())
                return false;
            pTIFF->Seek(aStripOffsets[nStrip]);
            for (sal_Int32 ny = 0; ny < nImageLength; ++ny)
            {
                for (sal_uInt32 np = 0; np < nPlanes; ++np)
                {
                    if ( ny / GetRowsPerStrip() + np * nStripsPerPlane > nStrip )
                    {
                        nStrip=ny/GetRowsPerStrip()+np*nStripsPerPlane;
                        if (nStrip >= aStripOffsets.size())
                            return false;
                        pTIFF->Seek(aStripOffsets[nStrip]);
                    }
                    sal_uInt32 nRowBytesLeft = nBytesPerRow;
                    if (np >= SAL_N_ELEMENTS(aMap))
                        return false;
                    sal_uInt8* pdst = getMapData(np);
                    do
                    {
                        sal_uInt8 nRecHeader(0);
                        pTIFF->ReadUChar(nRecHeader);
                        sal_uInt32 nRecCount;
                        if ((nRecHeader&0x80)==0)
                        {
                            nRecCount=0x00000001 + static_cast<sal_uInt32>(nRecHeader);
                            if ( nRecCount > nRowBytesLeft )
                                return false;
                            pTIFF->ReadBytes(pdst, nRecCount);
                            if (!pTIFF->good())
                                return false;
                            pdst+=nRecCount;
                            nRowBytesLeft-=nRecCount;
                        }
                        else if ( nRecHeader != 0x80 )
                        {
                            nRecCount = 0x000000101 - static_cast<sal_uInt32>(nRecHeader);
                            if ( nRecCount > nRowBytesLeft )
                            {
                                nRecCount = nRowBytesLeft;
                            }
                            sal_uInt8 nRecData(0);
                            pTIFF->ReadUChar( nRecData );
                            for (sal_uInt32 i = 0; i < nRecCount; ++i)
                                *(pdst++) = nRecData;
                            nRowBytesLeft -= nRecCount;
                        }
                    } while ( nRowBytesLeft != 0 );
                    if ( pTIFF->GetError() )
                        return false;
                }
                if ( !ConvertScanline( ny ) )
                    return false;
            }
        }
        else
            return false;
        return true;
    }

    sal_uInt32 TIFFReader::GetBits( const sal_uInt8 * pSrc, sal_uInt32 nBitsPos, sal_uInt32 nBitsCount)
    {
        sal_uInt32 nRes;
        if ( bByteSwap )
        {
            pSrc += ( nBitsPos >> 3 );
            nBitsPos &= 7;
            sal_uInt8 nDat = *pSrc;
            nRes = static_cast<sal_uInt32>( BYTESWAP( nDat ) & ( 0xff >> nBitsPos ) );

            if ( nBitsCount <= 8 - nBitsPos )
            {
                nRes >>= ( 8 - nBitsPos - nBitsCount );
            }
            else
            {
                pSrc++;
                nBitsCount -= 8 - nBitsPos;
                while ( nBitsCount >= 8 )
                {
                    nDat = *(pSrc++);
                    nRes = ( nRes << 8 ) | static_cast<sal_uInt32>(BYTESWAP( nDat ));
                    nBitsCount -= 8;
                }
                if ( nBitsCount > 0 )
                {
                    nDat = *pSrc;
                    nRes = ( nRes << nBitsCount ) | (static_cast<sal_uInt32>(BYTESWAP(nDat))>>(8-nBitsCount));
                }
            }
        }
        else
        {
            pSrc += ( nBitsPos >> 3 );
            nBitsPos &= 7;
            nRes = static_cast<sal_uInt32>((*pSrc)&(0xff>>nBitsPos));
            if ( nBitsCount <= 8 - nBitsPos )
            {
                nRes >>= ( 8 - nBitsPos - nBitsCount );
            }
            else
            {
                pSrc++;
                nBitsCount -= 8 - nBitsPos;
                while ( nBitsCount >= 8 )
                {
                    nRes = ( nRes << 8 ) | static_cast<sal_uInt32>(*(pSrc++));
                    nBitsCount -= 8;
                }
                if ( nBitsCount > 0 )
                    nRes = ( nRes << nBitsCount ) | (static_cast<sal_uInt32>(*pSrc)>>(8-nBitsCount));
            }
        }
        return nRes;
    }

    void TIFFReader::SetPixel(long nY, long nX, sal_uInt8 cIndex)
    {
        maBitmap[(maBitmapPixelSize.Width() * nY + nX) * (HasAlphaChannel() ? 4 : 3)] = cIndex;
        nLargestPixelIndex = std::max<int>(nLargestPixelIndex, cIndex);
    }

    void TIFFReader::SetPixel(long nY, long nX, Color c)
    {
        auto p = maBitmap.data() + ((maBitmapPixelSize.Width() * nY + nX) * (HasAlphaChannel() ? 4 : 3));
        *p = c.GetRed();
        p++;
        *p = c.GetGreen();
        p++;
        *p = c.GetBlue();
        if (HasAlphaChannel())
        {
            p++;
            *p = 0xff; // alpha
        }
    }

    void TIFFReader::SetPixelAlpha(long nY, long nX, sal_uInt8 nAlpha)
    {
        assert(HasAlphaChannel());
        maBitmap[((maBitmapPixelSize.Width() * nY + nX) * 4) + 3] = nAlpha;
    }

    bool TIFFReader::ConvertScanline(sal_Int32 nY)
    {
        sal_uInt32  nRed, nGreen, nBlue, ns, nVal;
        sal_uInt8   nByteVal;

        if ( nDstBitsPerPixel == 24 )
        {
            if ( nBitsPerSample == 8 && nSamplesPerPixel >= 3 &&
                 nPlanes == 1 && nPhotometricInterpretation == 2 )
            {
                sal_uInt8* pt = getMapData(0);

                // are the values being saved as difference?
                if ( 2 == nPredictor )
                {
                    sal_uInt8  nLRed = 0;
                    sal_uInt8  nLGreen = 0;
                    sal_uInt8  nLBlue = 0;
                    sal_uInt8  nLAlpha = 0;
                    for (sal_Int32 nx = 0; nx < nImageWidth; nx++, pt += nSamplesPerPixel)
                    {
                        nLRed = nLRed + pt[ 0 ];
                        nLGreen = nLGreen + pt[ 1 ];
                        nLBlue = nLBlue + pt[ 2 ];
                        SetPixel(nY, nx, Color(nLRed, nLGreen, nLBlue));
                        if (HasAlphaChannel())
                        {
                            nLAlpha = nLAlpha + pt[ 3 ];
                            SetPixelAlpha(nY, nx, ~nLAlpha);
                        }
                    }
                }
                else
                {
                    for (sal_Int32 nx = 0; nx < nImageWidth; nx++, pt += nSamplesPerPixel)
                    {
                        SetPixel(nY, nx, Color(pt[0], pt[1], pt[2]));
                        if (HasAlphaChannel())
                        {
                            sal_uInt8 nAlpha = pt[3];
                            SetPixelAlpha(nY, nx, ~nAlpha);
                        }
                    }
                }
            }
            else if ( nPhotometricInterpretation == 2 && nSamplesPerPixel >= 3 )
            {
                if ( nMaxSampleValue > nMinSampleValue )
                {
                    sal_uInt32 nMinMax = nMinSampleValue * 255 / ( nMaxSampleValue - nMinSampleValue );
                    for (sal_Int32 nx = 0; nx < nImageWidth; ++nx)
                    {
                        if ( nPlanes < 3 )
                        {
                            nRed = GetBits( getMapData(0), ( nx * nSamplesPerPixel + 0 ) * nBitsPerSample, nBitsPerSample );
                            nGreen = GetBits( getMapData(1), ( nx * nSamplesPerPixel + 1 ) * nBitsPerSample, nBitsPerSample );
                            nBlue = GetBits( getMapData(2), ( nx * nSamplesPerPixel + 2 ) * nBitsPerSample, nBitsPerSample );
                        }
                        else
                        {
                            nRed = GetBits( getMapData(0), nx * nBitsPerSample, nBitsPerSample );
                            nGreen = GetBits( getMapData(1), nx * nBitsPerSample, nBitsPerSample );
                            nBlue = GetBits( getMapData(2), nx * nBitsPerSample, nBitsPerSample );
                        }
                        SetPixel(nY, nx, Color(static_cast<sal_uInt8>(nRed - nMinMax), static_cast<sal_uInt8>(nGreen - nMinMax), static_cast<sal_uInt8>(nBlue - nMinMax)));
                    }
                }
            }
            else if ( nPhotometricInterpretation == 5 && nSamplesPerPixel == 3 )
            {
                if ( nMaxSampleValue > nMinSampleValue )
                {
                    sal_uInt32 nMinMax =  nMinSampleValue * 255 / ( nMaxSampleValue - nMinSampleValue );
                    for (sal_Int32 nx = 0; nx < nImageWidth; ++nx)
                    {
                        if ( nPlanes < 3 )
                        {
                            nRed = GetBits( getMapData(0), ( nx * nSamplesPerPixel + 0 ) * nBitsPerSample, nBitsPerSample );
                            nGreen = GetBits( getMapData(0), ( nx * nSamplesPerPixel + 1 ) * nBitsPerSample, nBitsPerSample );
                            nBlue = GetBits( getMapData(0), ( nx * nSamplesPerPixel + 2 ) * nBitsPerSample, nBitsPerSample );
                        }
                        else
                        {
                            nRed = GetBits( getMapData(0), nx * nBitsPerSample, nBitsPerSample );
                            nGreen = GetBits( getMapData(1), nx * nBitsPerSample, nBitsPerSample );
                            nBlue = GetBits( getMapData(2), nx * nBitsPerSample, nBitsPerSample );
                        }
                        nRed = 255 - static_cast<sal_uInt8>( nRed - nMinMax );
                        nGreen = 255 - static_cast<sal_uInt8>( nGreen - nMinMax );
                        nBlue = 255 - static_cast<sal_uInt8>( nBlue - nMinMax );
                        SetPixel(nY, nx, Color(static_cast<sal_uInt8>(nRed), static_cast<sal_uInt8>(nGreen), static_cast<sal_uInt8>(nBlue)));
                    }
                }
            }
            else if( nPhotometricInterpretation == 5 && nSamplesPerPixel == 4 )
            {
                if ( nMaxSampleValue > nMinSampleValue )
                {
                    sal_uInt8   nSamp[ 4 ];
                    sal_uInt8   nSampLast[ 4 ] = { 0, 0, 0, 0 };

                    for(sal_Int32 nx = 0; nx < nImageWidth; ++nx)
                    {
                        // are the values being saved as difference?
                        if( 2 == nPredictor )
                        {
                            for( ns = 0; ns < 4; ns++ )
                            {
                                if( nPlanes < 3 )
                                    nSampLast[ ns ] = nSampLast[ ns ] + static_cast<sal_uInt8>(GetBits( getMapData(0), ( nx * nSamplesPerPixel + ns ) * nBitsPerSample, nBitsPerSample ));
                                else
                                    nSampLast[ ns ] = nSampLast[ ns ] + static_cast<sal_uInt8>(GetBits( getMapData(ns), nx * nBitsPerSample, nBitsPerSample ));
                                nSamp[ ns ] = nSampLast[ ns ];
                            }
                        }
                        else
                        {
                            for( ns = 0; ns < 4; ns++ )
                            {
                                if( nPlanes < 3 )
                                    nSamp[ ns ] = static_cast<sal_uInt8>(GetBits( getMapData(0), ( nx * nSamplesPerPixel + ns ) * nBitsPerSample, nBitsPerSample ));
                                else
                                    nSamp[ ns ]= static_cast<sal_uInt8>(GetBits( getMapData(ns), nx * nBitsPerSample, nBitsPerSample ));
                            }
                        }
                        const long nBlack = nSamp[ 3 ];
                        nRed = static_cast<sal_uInt8>(std::max( 0L, 255L - ( ( static_cast<sal_Int32>(nSamp[ 0 ]) + nBlack - static_cast<sal_Int32>(nMinSampleValue << 1U ) ) *
                                    255L/static_cast<sal_Int32>(nMaxSampleValue-nMinSampleValue) ) ));
                        nGreen = static_cast<sal_uInt8>(std::max( 0L, 255L - ( ( static_cast<sal_Int32>(nSamp[ 1 ]) + nBlack - static_cast<sal_Int32>(nMinSampleValue << 1U ) ) *
                                    255L/static_cast<sal_Int32>(nMaxSampleValue-nMinSampleValue) ) ));
                        nBlue = static_cast<sal_uInt8>(std::max( 0L, 255L - ( ( static_cast<sal_Int32>(nSamp[ 2 ]) + nBlack - static_cast<sal_Int32>(nMinSampleValue << 1U ) ) *
                                    255L/static_cast<sal_Int32>(nMaxSampleValue-nMinSampleValue) ) ));
                        SetPixel(nY, nx, Color(static_cast<sal_uInt8>(nRed), static_cast<sal_uInt8>(nGreen), static_cast<sal_uInt8>(nBlue)));
                    }
                }
            }
        }
        else if ( nSamplesPerPixel == 1 && ( nPhotometricInterpretation <= 1 || nPhotometricInterpretation == 3 ) )
        {
            if ( nMaxSampleValue > nMinSampleValue )
            {
                sal_uInt32 nMinMax = ( ( 1 << nDstBitsPerPixel ) - 1 ) / ( nMaxSampleValue - nMinSampleValue );
                sal_uInt8* pt = getMapData(0);
                sal_uInt8* ptend = pt + nBytesPerRow;
                sal_uInt8 nShift;

                switch ( nDstBitsPerPixel )
                {
                    case 8 :
                    {
                        if (pt + nImageWidth > ptend)
                            return false;

                        if ( bByteSwap )
                        {
                            if ( nPredictor == 2 )
                            {
                                sal_uInt8 nLast = 0;
                                for (sal_Int32 nx = 0; nx < nImageWidth; ++nx)
                                {
                                    nLast += nx == 0 ? BYTESWAP( *pt++ ) : *pt++;
                                    SetPixel(nY, nx, nLast);
                                }
                            }
                            else
                            {
                                for (sal_Int32 nx = 0; nx < nImageWidth; ++nx)
                                {
                                    sal_uInt8 nLast = *pt++;
                                    SetPixel(nY, nx, static_cast<sal_uInt8>( (BYTESWAP(static_cast<sal_uInt32>(nLast)) - nMinSampleValue) * nMinMax ));
                                }
                            }
                        }
                        else
                        {
                            if ( nPredictor == 2 )
                            {
                                sal_uInt8 nLast = 0;
                                for (sal_Int32 nx = 0; nx < nImageWidth; ++nx)
                                {
                                    nLast += *pt++;
                                    SetPixel(nY, nx, nLast);
                                }
                            }
                            else
                            {
                                for (sal_Int32 nx = 0; nx < nImageWidth; ++nx)
                                {
                                    SetPixel(nY, nx, static_cast<sal_uInt8>( (static_cast<sal_uInt32>(*pt++) - nMinSampleValue) * nMinMax ));
                                }
                            }
                        }
                    }
                    break;

                    case 7 :
                    case 6 :
                    case 5 :
                    case 4 :
                    case 3 :
                    case 2 :
                    {
                        for (sal_Int32 nx = 0; nx < nImageWidth; ++nx)
                        {
                            nVal = ( GetBits( pt, nx * nBitsPerSample, nBitsPerSample ) - nMinSampleValue ) * nMinMax;
                            SetPixel(nY, nx, static_cast<sal_uInt8>(nVal));
                        }
                    }
                    break;

                    case 1 :
                    {
                        sal_uInt32 nByteCount = nImageWidth >> 3;

                        sal_uInt32 nBytesNeeded = nByteCount;
                        if (nImageWidth & 7)
                            ++nBytesNeeded;
                        if (pt + nBytesNeeded > ptend)
                            return false;

                        if ( bByteSwap )
                        {
                            sal_Int32 nx = 0;
                            while (nByteCount--)
                            {
                                nByteVal = *pt++;
                                SetPixel(nY, nx++, nByteVal & 1);
                                nByteVal >>= 1;
                                SetPixel(nY, nx++, nByteVal & 1);
                                nByteVal >>= 1;
                                SetPixel(nY, nx++, nByteVal & 1);
                                nByteVal >>= 1;
                                SetPixel(nY, nx++, nByteVal & 1);
                                nByteVal >>= 1;
                                SetPixel(nY, nx++, nByteVal & 1);
                                nByteVal >>= 1;
                                SetPixel(nY, nx++, nByteVal & 1);
                                nByteVal >>= 1;
                                SetPixel(nY, nx++, nByteVal & 1);
                                nByteVal >>= 1;
                                SetPixel(nY, nx++, nByteVal);
                            }
                            if ( nImageWidth & 7 )
                            {
                                nByteVal = *pt++;
                                while ( nx < nImageWidth )
                                {
                                    SetPixel(nY, nx++, nByteVal & 1);
                                    nByteVal >>= 1;
                                }
                            }
                        }
                        else
                        {
                            sal_Int32 nx = 7;
                            while (nByteCount--)
                            {
                                nByteVal = *pt++;
                                SetPixel(nY, nx, nByteVal & 1);
                                nByteVal >>= 1;
                                SetPixel(nY, --nx, nByteVal & 1);
                                nByteVal >>= 1;
                                SetPixel(nY, --nx, nByteVal & 1);
                                nByteVal >>= 1;
                                SetPixel(nY, --nx, nByteVal & 1);
                                nByteVal >>= 1;
                                SetPixel(nY, --nx, nByteVal & 1);
                                nByteVal >>= 1;
                                SetPixel(nY, --nx, nByteVal & 1);
                                nByteVal >>= 1;
                                SetPixel(nY, --nx, nByteVal & 1);
                                nByteVal >>= 1;
                                SetPixel(nY, --nx, nByteVal);
                                nx += 15;
                            }
                            if ( nImageWidth & 7 )
                            {
                                nx -= 7;
                                nByteVal = *pt++;
                                nShift = 7;
                                while ( nx < nImageWidth )
                                {
                                    SetPixel(nY, nx++, ( nByteVal >> nShift ) & 1);
                                }
                            }
                        }
                    }
                    break;

                    default :
                        return false;
                }
            }
        }
        else if ( ( nSamplesPerPixel == 2 ) && ( nBitsPerSample == 8 ) &&
            ( nPlanarConfiguration == 1 ) && aColorMap.empty() )               // grayscale
        {
            if ( nMaxSampleValue > nMinSampleValue )
            {
                sal_uInt32 nMinMax = ( ( 1 << 8 /*nDstBitsPerPixel*/ ) - 1 ) / ( nMaxSampleValue - nMinSampleValue );
                sal_uInt8*  pt = getMapData(0);
                for (sal_Int32 nx = 0; nx < nImageWidth; nx++, pt += 2 )
                {
                    SetPixel(nY, nx, static_cast<sal_uInt8>( (static_cast<sal_uInt32>(*pt) - nMinSampleValue) * nMinMax));
                }
            }
        }
        else
            return false;
        return true;
    }

    void TIFFReader::MakePalCol()
    {
        if ( nDstBitsPerPixel <= 8 )
        {
            aColorMap.resize(256);
            if ( nPhotometricInterpretation <= 1 )
            {
                nNumColors = sal_uInt32(1) << nBitsPerSample;
                if ( nNumColors > 256 )
                    nNumColors = 256;

                if (nLargestPixelIndex >= static_cast<int>(nNumColors))
                {
                    SAL_WARN("filter.tiff", "palette has less entries that largest index used. Expanding palette to match");
                    nNumColors = nLargestPixelIndex + 1;
                }

                for (sal_uInt32 i = 0; i < nNumColors; ++i)
                {
                    sal_uInt32 nVal = ( i * 255 / ( nNumColors - 1 ) ) & 0xff;
                    sal_uInt32 n0RGB = nVal | ( nVal << 8 ) | ( nVal << 16 );
                    if ( nPhotometricInterpretation == 1 )
                        aColorMap[i] = n0RGB;
                    else
                        aColorMap[nNumColors - i - 1] = n0RGB;
                }
            }
            mvPalette.resize(std::max<sal_uInt16>(nNumColors, mvPalette.size()));
            for (sal_uInt32 i = 0; i < nNumColors; ++i)
            {
                mvPalette[i] = Color( static_cast<sal_uInt8>( aColorMap[ i ] >> 16 ),
                    static_cast<sal_uInt8>( aColorMap[ i ] >> 8 ), static_cast<sal_uInt8>(aColorMap[ i ]) );
            }
        }

        if ( fXResolution > 1.0 && fYResolution > 1.0 && ( nResolutionUnit == 2 || nResolutionUnit == 3 ) )
        {
            sal_uInt32 nRX, nRY;
            if (nResolutionUnit==2)
            {
                nRX=static_cast<sal_uInt32>(fXResolution+0.5);
                nRY=static_cast<sal_uInt32>(fYResolution+0.5);
            }
            else
            {
                nRX=static_cast<sal_uInt32>(fXResolution*2.54+0.5);
                nRY=static_cast<sal_uInt32>(fYResolution*2.54+0.5);
            }
            MapMode aMapMode(MapUnit::MapInch,Point(0,0),Fraction(1,nRX),Fraction(1,nRY));
            maBitmapPrefMapMode = aMapMode;
            maBitmapPrefSize = Size(nImageWidth,nImageLength);
        }
    }

    void TIFFReader::ReadHeader()
    {
        sal_uInt8 nbyte1(0), nbyte2(0);
        sal_uInt16 nushort(0);

        pTIFF->ReadUChar( nbyte1 );
        if ( nbyte1 == 'I' )
            pTIFF->SetEndian( SvStreamEndian::LITTLE );
        else
            pTIFF->SetEndian( SvStreamEndian::BIG );

        pTIFF->ReadUChar( nbyte2 ).ReadUInt16( nushort );
        if ( nbyte1 != nbyte2 || ( nbyte1 != 'I' && nbyte1 != 'M' ) || nushort != 0x002a )
            bStatus = false;
    }

    bool TIFFReader::HasAlphaChannel() const
    {
        /*There are undoubtedly more variants we could support, but keep it simple for now*/
        return (
                 nDstBitsPerPixel == 24 &&
                 nBitsPerSample == 8 &&
                 nSamplesPerPixel >= 4 &&
                 nPlanes == 1 &&
                 nPhotometricInterpretation == 2
               );
    }

    namespace
    {
        Color SanitizePaletteIndex(sal_uInt8 nIndex, const std::vector<Color>& rPalette)
        {
            const size_t nPaletteEntryCount = rPalette.size();
            if (nPaletteEntryCount && nIndex >= nPaletteEntryCount)
            {
                auto nSanitizedIndex = nIndex % nPaletteEntryCount;
                SAL_WARN_IF(nIndex != nSanitizedIndex, "vcl", "invalid colormap index: "
                            << static_cast<unsigned int>(nIndex) << ", colormap len is: "
                            << nPaletteEntryCount);
                nIndex = nSanitizedIndex;
            }

            return rPalette[nIndex];
        }
    }

    bool TIFFReader::ReadTIFF(SvStream & rTIFF, Graphic & rGraphic )
    {
        sal_uInt16  i, nNumTags(0), nTagType(0);
        sal_uInt32 nFirstIfd(0), nDataLen;

        bStatus = true;

        pTIFF = &rTIFF;
        sal_uInt64 nMaxPos = nOrigPos = pTIFF->Tell();
        nEndOfFile = nOrigPos + pTIFF->remainingSize();
        // number format of pTIFF at the beginning
        SvStreamEndian nOrigNumberFormat = pTIFF->GetEndian();

        // read header:
        ReadHeader();

        // read first IFD:
        pTIFF->ReadUInt32( nFirstIfd );

        if( !nFirstIfd || pTIFF->GetError() )
            bStatus = false;

        if ( bStatus )
        {
            sal_uInt32 nOffset = nFirstIfd;

            std::vector<sal_uInt32> aSeenOffsets;
            // calculate length of TIFF file
            do
            {
                if (std::find(aSeenOffsets.begin(), aSeenOffsets.end(), nOffset) != aSeenOffsets.end())
                {
                    SAL_WARN("filter.tiff", "Parsing error: " << nOffset <<
                             " already processed, format loop");
                    bStatus = false;
                    break;
                }
                pTIFF->Seek(nOrigPos + nOffset);
                aSeenOffsets.push_back(nOffset);

                if( pTIFF->GetError() )
                {
                    pTIFF->ResetError();
                    break;
                };
                nMaxPos = std::max( pTIFF->Tell(), nMaxPos );

                pTIFF->ReadUInt16( nNumTags );

                const size_t nMinRecordSize = 12;
                const size_t nMaxRecords = pTIFF->remainingSize() / nMinRecordSize;
                if (nNumTags > nMaxRecords)
                {
                    SAL_WARN("filter.tiff", "Parsing error: " << nMaxRecords <<
                             " max possible entries, but " << nNumTags << " claimed, truncating");
                    nNumTags = nMaxRecords;
                }

                // loop through tags:
                for( i = 0; i < nNumTags; i++ )
                {
                    nTagType = 0;
                    nDataType = USHRT_MAX;
                    nDataLen = 0;
                    nOffset = 0;
                    pTIFF->ReadUInt16( nTagType ).ReadUInt16( nDataType ).ReadUInt32( nDataLen ).ReadUInt32( nOffset );

                    if( DataTypeSize() * nDataLen > 4 )
                        nMaxPos = std::max(nOrigPos + nOffset + DataTypeSize() * nDataLen, nMaxPos);
                }
                pTIFF->ReadUInt32( nOffset );
                if (!pTIFF->good())
                    nOffset = 0;

                nMaxPos = std::max( pTIFF->Tell(), nMaxPos );
                if ( !nOffset )
                    nMaxPos = std::max( pTIFF->Tell(), nMaxPos );
            }
            while( nOffset );

            std::vector<sal_uInt32> aSeenIfds;

            for ( sal_uInt32 nNextIfd = nFirstIfd; nNextIfd && bStatus; )
            {
                if (std::find(aSeenIfds.begin(), aSeenIfds.end(), nNextIfd) != aSeenIfds.end())
                {
                    SAL_WARN("filter.tiff", "Parsing error: " << nNextIfd <<
                             " already processed, format loop");
                    bStatus = false;
                    break;
                }
                pTIFF->Seek(nOrigPos + nNextIfd);
                aSeenIfds.push_back(nNextIfd);
                {
                    bByteSwap = false;

                    nNewSubFile = 0;
                    nSubFile = 0;
                    nImageWidth = 0;
                    nImageLength = 0;
                    nBitsPerSample = 1;                         // default value according to the documentation
                    nCompression = 1;
                    nPhotometricInterpretation = 0;
                    nThresholding = 1;                          // default value according to the documentation
                    nCellWidth = 1;
                    nCellLength = 1;
                    nFillOrder = 1;                             // default value according to the documentation
                    nOrientation = 1;
                    nSamplesPerPixel = 1;                       // default value according to the documentation
                    nRowsPerStrip = 0xffffffff;                 // default value according to the documentation
                    nMinSampleValue = 0;                        // default value according to the documentation
                    nMaxSampleValue = 0;
                    fXResolution = 0.0;
                    fYResolution = 0.0;
                    nPlanarConfiguration = 1;
                    nGroup3Options = 0;                         // default value according to the documentation
                    nGroup4Options = 0;                         // default value according to the documentation
                    nResolutionUnit = 2;                        // default value according to the documentation
                    nPredictor = 1;
                    nNumColors = 0;

                    aStripOffsets.clear();
                    aStripByteCounts.clear();
                    for (auto& j : aMap)
                        j.clear();

                    pTIFF->ReadUInt16( nNumTags );
                    sal_uInt64 nPos = pTIFF->Tell();

                    const size_t nMinRecordSize = 8;
                    const size_t nMaxRecords = pTIFF->remainingSize() / nMinRecordSize;
                    if (nNumTags > nMaxRecords)
                    {
                        SAL_WARN("filter.tiff", "Parsing error: " << nMaxRecords <<
                                 " max possible entries, but " << nNumTags << " claimed, truncating");
                        nNumTags = nMaxRecords;
                    }

                    for( i = 0; i < nNumTags; i++ )
                    {
                        pTIFF->ReadUInt16( nTagType ).ReadUInt16( nDataType ).ReadUInt32( nDataLen );

                        if( DataTypeSize() * nDataLen > 4 )
                        {
                            pTIFF->ReadUInt32( nOffset );
                            if (!checkSeek(*pTIFF, nOrigPos + nOffset))
                            {
                                bStatus = false;
                                break;
                            }
                        }
                        ReadTagData( nTagType, nDataLen );
                        nPos += 12; pTIFF->Seek( nPos );

                        if ( pTIFF->GetError() )
                            bStatus = false;

                        if ( !bStatus )
                            break;
                    }
                    pTIFF->ReadUInt32( nNextIfd );
                    if (!pTIFF->good())
                        nNextIfd = 0;
                }
                if ( !nBitsPerSample || ( nBitsPerSample > 32 ) )
                    bStatus = false;
                if (nImageWidth <= 0 || nImageLength <= 0)
                    bStatus = false;
                if ( bStatus )
                {
                    nLargestPixelIndex = -1;
                    if ( nMaxSampleValue == 0 )
                    {
                        if ( nBitsPerSample == 32 )         // sj: i93300, compiler bug, 1 << 32 gives 1 one 32bit windows platforms,
                            nMaxSampleValue = 0xffffffff;   // (up from 80286 only the lower 5 bits are used when shifting a 32bit register)
                        else
                        {
                            nMaxSampleValue = (1U << nBitsPerSample) - 1;
                        }
                    }
                    if ( nPhotometricInterpretation == 2 || nPhotometricInterpretation == 5 || nPhotometricInterpretation == 6 )
                        nDstBitsPerPixel = 24;
                    else if ( nBitsPerSample*nSamplesPerPixel <= 1 )
                        nDstBitsPerPixel = 1;
                    else if ( nBitsPerSample*nSamplesPerPixel <= 4 )
                        nDstBitsPerPixel = 4;
                    else
                        nDstBitsPerPixel = 8;

                    if ( nPlanarConfiguration == 1 )
                        nPlanes = 1;
                    else
                        nPlanes = nSamplesPerPixel;

                    bStatus = nPlanes != 0;
                }

                sal_uInt32 nDiv = GetRowsPerStrip();

                if ( bStatus )
                {
                    bStatus = (nDiv != 0);
                }

                if ( bStatus )
                {
                    if ( ( nFillOrder == 2 ) && ( nCompression != 5 ) )     // in the LZW mode bits are already being inverted
                        bByteSwap = true;
                    nStripsPerPlane = ( nImageLength - 1 ) / nDiv + 1;
                }

                if (bStatus)
                {
                    bStatus = nSamplesPerPixel != 0;
                }

                if ( bStatus )
                {
                    sal_uInt64 nRowSize = (static_cast<sal_uInt64>(nImageWidth) * nSamplesPerPixel / nPlanes * nBitsPerSample + 7) >> 3;
                    if (nRowSize > SAL_MAX_INT32 / SAL_N_ELEMENTS(aMap))
                    {
                        SAL_WARN("filter.tiff", "Ludicrous row size of: " << nRowSize << " required");
                        bStatus = false;
                    }
                    else
                        nBytesPerRow = nRowSize;
                }

                if (bStatus)
                {
                    //sanity check consider ReadMap condition for last row and
                    //last plane
                    if (nCompression == 1 || nCompression == 32771)
                    {
                        sal_uInt32 nStripBytesPerRow;
                        if (nCompression == 1)
                            nStripBytesPerRow = nBytesPerRow;
                        else
                            nStripBytesPerRow = ( nBytesPerRow + 1 ) & 0xfffffffe;
                        sal_uInt32 np = nPlanes - 1;
                        if (np >= SAL_N_ELEMENTS(aMap))
                            bStatus = false;
                        sal_Int32 ny = nImageLength - 1;
                        sal_uInt32 nStrip(0);
                        nDiv = GetRowsPerStrip();
                        if (bStatus)
                            bStatus = nDiv != 0;
                        if (bStatus)
                        {
                            nStrip = ny / nDiv + np * nStripsPerPlane;
                            if (nStrip >= aStripOffsets.size())
                                bStatus = false;
                        }
                        if (bStatus)
                        {
                            auto nStart = aStripOffsets[ nStrip ] + ( ny % GetRowsPerStrip() ) * nStripBytesPerRow;
                            auto nEnd = nStart + nBytesPerRow;
                            if (nEnd > nEndOfFile)
                                bStatus = false;
                        }
                    }
                    else if (nCompression == 2 || nCompression == 3 || nCompression == 4)
                    {
                        if (nCompression == 3 && nGroup3Options & 0xfffffffa)
                            bStatus = false;
                        else if (nCompression == 4 && nGroup4Options & 0xffffffff)
                            bStatus = false;
                        sal_uInt32 np = nPlanes - 1;
                        if (np >= SAL_N_ELEMENTS(aMap))
                            bStatus = false;
                        sal_Int32 ny = nImageLength - 1;
                        sal_uInt32 nStrip(0);
                        nDiv = GetRowsPerStrip();
                        if (bStatus)
                            bStatus = nDiv != 0;
                        if (bStatus)
                        {
                            nStrip = ny / nDiv + np * nStripsPerPlane;
                            if (nStrip >= aStripOffsets.size())
                                bStatus = false;
                        }
                        if (bStatus)
                        {
                            auto nStart = aStripOffsets[nStrip];
                            if (nStart > nEndOfFile)
                                bStatus = false;
                        }

                        if (bStatus)
                        {
                            sal_uLong nTargetBits = nImageWidth * nBitsPerSample * nSamplesPerPixel / nPlanes;
                            if (nTargetBits > SAL_MAX_UINT16)
                                bStatus = false;
                        }
                    }
                    else if (nCompression == 5)
                    {
                        sal_uInt32 np = nPlanes - 1;
                        if (np >= SAL_N_ELEMENTS(aMap))
                            bStatus = false;
                        sal_Int32 ny = nImageLength - 1;
                        sal_uInt32 nStrip(0);
                        nDiv = GetRowsPerStrip();
                        if (bStatus)
                            bStatus = nDiv != 0;
                        if (bStatus)
                        {
                            nStrip = ny / nDiv + np * nStripsPerPlane;
                            if (nStrip >= aStripOffsets.size())
                                bStatus = false;
                        }
                        if (bStatus)
                        {
                            auto nStart = aStripOffsets[nStrip];
                            if (nStart > nEndOfFile)
                                bStatus = false;
                        }
                    }
                    else if (nCompression == 32773)
                    {
                    }
                    else
                    {
                        bStatus = false;
                    }
                }

                sal_Int32 nImageDataSize(0);
                if (bStatus)
                {
                    if (o3tl::checked_multiply<sal_Int32>(nImageWidth, nImageLength, nImageDataSize) ||
                        o3tl::checked_multiply<sal_Int32>(nImageDataSize, (HasAlphaChannel() ? 4 : 3), nImageDataSize) ||
                        nImageDataSize > SAL_MAX_INT32/2)
                    {
                        bStatus = false;
                    }
                }

                if ( bStatus )
                {
                    maBitmapPixelSize = Size(nImageWidth, nImageLength);
                    maBitmap.resize(nImageDataSize, 0);

                    if (bStatus && ReadMap())
                    {
                        nMaxPos = std::max( pTIFF->Tell(), nMaxPos );
                        MakePalCol();
                        nMaxPos = std::max( pTIFF->Tell(), nMaxPos );
                        // convert palette-ized images to 24-bit color
                        if (!mvPalette.empty())
                        {
                            for (sal_Int32 nY = 0; nY < nImageLength; ++nY)
                            {
                                for (sal_Int32 nX = 0; nX < nImageWidth; ++nX)
                                {
                                    auto p = maBitmap.data() + ((maBitmapPixelSize.Width() * nY + nX) * 3);
                                    auto c = SanitizePaletteIndex(*p, mvPalette);
                                    *p = c.GetRed();
                                    p++;
                                    *p = c.GetGreen();
                                    p++;
                                    *p = c.GetBlue();
                                }
                            }
                        }
                    }
                    else
                        bStatus = false;

                    if ( bStatus )
                    {
                        BitmapEx aImage = vcl::bitmap::CreateFromData(maBitmap.data(), nImageWidth, nImageLength,
                                nImageWidth * (HasAlphaChannel() ? 4 : 3), // scanline bytes
                                HasAlphaChannel() ? 32 : 24);
                        aImage.SetPrefMapMode(maBitmapPrefMapMode);
                        aImage.SetPrefSize(maBitmapPrefSize);

                        AnimationBitmap aAnimationBitmap( aImage, Point( 0, 0 ), maBitmapPixelSize,
                                                          ANIMATION_TIMEOUT_ON_CLICK, Disposal::Back );

                        aAnimation.Insert( aAnimationBitmap );
                    }
                }

                // Clean up:
                for (auto& j : aMap)
                    j.clear();
                aColorMap.clear();
                aStripOffsets.clear();
                aStripByteCounts.clear();
            }
        }

        // seek to end of TIFF if succeeded
        pTIFF->SetEndian( nOrigNumberFormat );
        pTIFF->Seek(bStatus ? nMaxPos : nOrigPos);

        if ( aAnimation.Count() )
        {
            if ( aAnimation.Count() == 1 )
                rGraphic = aAnimation.GetBitmapEx();
            else
                rGraphic = aAnimation;  //aBitmap;

            return true;
        }
        else
            return false;
    }

    //================== GraphicImport - the exported function ================

    extern "C" SAL_DLLPUBLIC_EXPORT bool
    itiGraphicImport( SvStream & rStream, Graphic & rGraphic, FilterConfigItem* )
    {
        TIFFReader aTIFFReader;
        try
        {
            return aTIFFReader.ReadTIFF(rStream, rGraphic);
        }
        catch (const std::bad_alloc &)
        {
            return false;
        }
    }

    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

↑ V581 The conditional expressions of the 'if' statements situated alongside each other are identical. Check lines: 1462, 1469.