    /* -*- 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 <sal/config.h>
    #include <sal/log.hxx>

    #include <comphelper/base64.hxx>
    #include <comphelper/graphicmimetype.hxx>
    #include <tools/poly.hxx>
    #include <vcl/bitmapaccess.hxx>
    #include <vcl/virdev.hxx>
    #include <vcl/wrkwin.hxx>
    #include <svl/solar.hrc>
    #include <sfx2/docfile.hxx>
    #include <sfx2/app.hxx>
    #include <svx/xoutbmp.hxx>
    #include <vcl/dibtools.hxx>
    #include <vcl/FilterConfigItem.hxx>
    #include <vcl/graphicfilter.hxx>
    #include <vcl/cvtgrf.hxx>
    #include <sax/tools/converter.hxx>
    #include <memory>

    #define FORMAT_BMP  "bmp"
    #define FORMAT_GIF  "gif"
    #define FORMAT_JPG  "jpg"
    #define FORMAT_PNG  "png"

    using namespace com::sun::star;

    GraphicFilter* XOutBitmap::pGrfFilter = nullptr;

    Animation XOutBitmap::MirrorAnimation( const Animation& rAnimation, bool bHMirr, bool bVMirr )
    {
        Animation aNewAnim( rAnimation );

        if( bHMirr || bVMirr )
        {
            const Size& rGlobalSize = aNewAnim.GetDisplaySizePixel();
            BmpMirrorFlags nMirrorFlags = BmpMirrorFlags::NONE;

            if( bHMirr )
                nMirrorFlags |= BmpMirrorFlags::Horizontal;

            if( bVMirr )
                nMirrorFlags |= BmpMirrorFlags::Vertical;

            for( sal_uInt16 i = 0, nCount = aNewAnim.Count(); i < nCount; i++ )
            {
                AnimationBitmap aAnimBmp( aNewAnim.Get( i ) );

                // mirror the BitmapEx
                aAnimBmp.aBmpEx.Mirror( nMirrorFlags );

                // Adjust the positions inside the whole bitmap
                if( bHMirr )
                    aAnimBmp.aPosPix.setX( rGlobalSize.Width() - aAnimBmp.aPosPix.X() -
                                           aAnimBmp.aSizePix.Width() );

                if( bVMirr )
                    aAnimBmp.aPosPix.setY( rGlobalSize.Height() - aAnimBmp.aPosPix.Y() -
                                           aAnimBmp.aSizePix.Height() );

                aNewAnim.Replace( aAnimBmp, i );
            }
        }

        return aNewAnim;
    }

    Graphic XOutBitmap::MirrorGraphic( const Graphic& rGraphic, const BmpMirrorFlags nMirrorFlags )
    {
        Graphic aRetGraphic;

        if( nMirrorFlags != BmpMirrorFlags::NONE )
        {
            if( rGraphic.IsAnimated() )
            {
                aRetGraphic = MirrorAnimation( rGraphic.GetAnimation(),
                                               bool( nMirrorFlags & BmpMirrorFlags::Horizontal ),
                                               bool( nMirrorFlags & BmpMirrorFlags::Vertical ) );
            }
            else
            {
                BitmapEx aBmp( rGraphic.GetBitmapEx() );
                aBmp.Mirror( nMirrorFlags );
                aRetGraphic = aBmp;
            }
        }
        else
            aRetGraphic = rGraphic;

        return aRetGraphic;
    }

    ErrCode XOutBitmap::WriteGraphic( const Graphic& rGraphic, OUString& rFileName,
                                     const OUString& rFilterName, const XOutFlags nFlags,
                                     const Size* pMtfSize_100TH_MM,
                                     const css::uno::Sequence< css::beans::PropertyValue >* pFilterData )
    {
        if( rGraphic.GetType() != GraphicType::NONE )
        {
            INetURLObject   aURL( rFileName );
            Graphic         aGraphic;
            OUString        aExt;
            GraphicFilter&  rFilter = GraphicFilter::GetGraphicFilter();
            ErrCode         nErr = ERRCODE_GRFILTER_FILTERERROR;
            sal_uInt16      nFilter = GRFILTER_FORMAT_NOTFOUND;
            bool            bTransparent = rGraphic.IsTransparent(), bAnimated = rGraphic.IsAnimated();

            DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "XOutBitmap::WriteGraphic(...): invalid URL" );

            // calculate correct file name
            if( !( nFlags & XOutFlags::DontExpandFilename ) )
            {
                OUString aName( aURL.getBase() );
                aName += "_";
                aName += aURL.getExtension();
                aName += "_";
                OUString aStr( OUString::number( rGraphic.GetChecksum(), 16 ) );
                if ( aStr[0] == '-' )
                    aStr = "m" + aStr.copy(1);
                aName += aStr;
                aURL.setBase( aName );
            }

            // #i121128# use shortcut to write Vector Graphic Data data in original form (if possible)
            const VectorGraphicDataPtr& aVectorGraphicDataPtr(rGraphic.getVectorGraphicData());

            if(aVectorGraphicDataPtr.get()
                && aVectorGraphicDataPtr->getVectorGraphicDataArrayLength())
            {
                const bool bIsSvg(rFilterName.equalsIgnoreAsciiCase("svg") && VectorGraphicDataType::Svg == aVectorGraphicDataPtr->getVectorGraphicDataType());
                const bool bIsWmf(rFilterName.equalsIgnoreAsciiCase("wmf") && VectorGraphicDataType::Wmf == aVectorGraphicDataPtr->getVectorGraphicDataType());
                const bool bIsEmf(rFilterName.equalsIgnoreAsciiCase("emf") && VectorGraphicDataType::Emf == aVectorGraphicDataPtr->getVectorGraphicDataType());

                if (bIsSvg || bIsWmf || bIsEmf)
                {
                    if (!(nFlags & XOutFlags::DontAddExtension))
                    {
                        aURL.setExtension(rFilterName);
                    }

                    rFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
                    SfxMedium aMedium(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::WRITE | StreamMode::SHARE_DENYNONE | StreamMode::TRUNC);
                    SvStream* pOStm = aMedium.GetOutStream();

                    if (pOStm)
                    {
                        pOStm->WriteBytes(aVectorGraphicDataPtr->getVectorGraphicDataArray().getConstArray(), aVectorGraphicDataPtr->getVectorGraphicDataArrayLength());
                        aMedium.Commit();

                        if (!aMedium.GetError())
                        {
                            nErr = ERRCODE_NONE;
                        }
                    }
                }
            }

            // Write PDF data in original form if possible.
            if (rGraphic.hasPdfData() && rFilterName.equalsIgnoreAsciiCase("pdf"))
            {
                if (!(nFlags & XOutFlags::DontAddExtension))
                    aURL.setExtension(rFilterName);

                rFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
                SfxMedium aMedium(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::WRITE|StreamMode::SHARE_DENYNONE|StreamMode::TRUNC);
                if (SvStream* pOutStream = aMedium.GetOutStream())
                {
                    const std::shared_ptr<uno::Sequence<sal_Int8>>& rPdfData = rGraphic.getPdfData();
                    pOutStream->WriteBytes(rPdfData->getConstArray(), rPdfData->getLength());
                    aMedium.Commit();
                    if (!aMedium.GetError())
                        nErr = ERRCODE_NONE;
                }
            }

            if( ERRCODE_NONE != nErr )
            {
                if( ( nFlags & XOutFlags::UseNativeIfPossible ) &&
                    !( nFlags & XOutFlags::MirrorHorz ) &&
                    !( nFlags & XOutFlags::MirrorVert ) &&
                    ( rGraphic.GetType() != GraphicType::GdiMetafile ) && rGraphic.IsGfxLink() )
                {
                    // try to write native link
                    const GfxLink aGfxLink( rGraphic.GetGfxLink() );

                    switch( aGfxLink.GetType() )
                    {
                        case GfxLinkType::NativeGif: aExt = FORMAT_GIF; break;

                        // #i15508# added BMP type for better exports (no call/trigger found, prob used in HTML export)
                        case GfxLinkType::NativeBmp: aExt = FORMAT_BMP; break;

                        case GfxLinkType::NativeJpg: aExt = FORMAT_JPG; break;
                        case GfxLinkType::NativePng: aExt = FORMAT_PNG; break;

                        default:
                        break;
                    }

                    if( !aExt.isEmpty() )
                    {
                        if( !(nFlags & XOutFlags::DontAddExtension) )
                            aURL.setExtension( aExt );
                        rFileName = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );

                        SfxMedium   aMedium(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::WRITE | StreamMode::SHARE_DENYNONE | StreamMode::TRUNC);
                        SvStream*   pOStm = aMedium.GetOutStream();

                        if( pOStm && aGfxLink.GetDataSize() && aGfxLink.GetData() )
                        {
                            pOStm->WriteBytes(aGfxLink.GetData(), aGfxLink.GetDataSize());
                            aMedium.Commit();

                            if( !aMedium.GetError() )
                                nErr = ERRCODE_NONE;
                        }
                    }
                }
            }

            if( ERRCODE_NONE != nErr )
            {
                OUString  aFilter( rFilterName );
                bool    bWriteTransGrf = ( aFilter.equalsIgnoreAsciiCase( "transgrf" ) ) ||
                                         ( aFilter.equalsIgnoreAsciiCase( "gif" ) ) ||
                                         ( nFlags & XOutFlags::UseGifIfPossible ) ||
                                         ( ( nFlags & XOutFlags::UseGifIfSensible ) && ( bAnimated || bTransparent ) );

                // get filter and extension
                if( bWriteTransGrf )
                    aFilter = FORMAT_GIF;

                nFilter = rFilter.GetExportFormatNumberForShortName( aFilter );

                if( GRFILTER_FORMAT_NOTFOUND == nFilter )
                {
                    nFilter = rFilter.GetExportFormatNumberForShortName( FORMAT_PNG );

                    if( GRFILTER_FORMAT_NOTFOUND == nFilter )
                        nFilter = rFilter.GetExportFormatNumberForShortName( FORMAT_BMP );
                }

                if( GRFILTER_FORMAT_NOTFOUND != nFilter )
                {
                    aExt = rFilter.GetExportFormatShortName( nFilter ).toAsciiLowerCase();

                    if( bWriteTransGrf )
                    {
                        if( bAnimated  )
                            aGraphic = rGraphic;
                        else
                        {
                            if( pMtfSize_100TH_MM && ( rGraphic.GetType() != GraphicType::Bitmap ) )
                            {
                                ScopedVclPtrInstance< VirtualDevice > pVDev;
                                const Size aSize(pVDev->LogicToPixel(*pMtfSize_100TH_MM, MapMode(MapUnit::Map100thMM)));

                                if( pVDev->SetOutputSizePixel( aSize ) )
                                {
                                    const Wallpaper aWallpaper( pVDev->GetBackground() );
                                    const Point     aPt;

                                    pVDev->SetBackground( Wallpaper( COL_BLACK ) );
                                    pVDev->Erase();
                                    rGraphic.Draw( pVDev.get(), aPt, aSize );

                                    const Bitmap aBitmap( pVDev->GetBitmap( aPt, aSize ) );

                                    pVDev->SetBackground( aWallpaper );
                                    pVDev->Erase();
                                    rGraphic.Draw( pVDev.get(), aPt, aSize );

                                    pVDev->SetRasterOp( RasterOp::Xor );
                                    pVDev->DrawBitmap( aPt, aSize, aBitmap );
                                    aGraphic = BitmapEx( aBitmap, pVDev->GetBitmap( aPt, aSize ) );
                                }
                                else
                                    aGraphic = rGraphic.GetBitmapEx();
                            }
                            else
                                aGraphic = rGraphic.GetBitmapEx();
                        }
                    }
                    else
                    {
                        if( pMtfSize_100TH_MM && ( rGraphic.GetType() != GraphicType::Bitmap ) )
                        {
                            ScopedVclPtrInstance< VirtualDevice > pVDev;
                            const Size aSize(pVDev->LogicToPixel(*pMtfSize_100TH_MM, MapMode(MapUnit::Map100thMM)));

                            if( pVDev->SetOutputSizePixel( aSize ) )
                            {
                                rGraphic.Draw( pVDev.get(), Point(), aSize );
                                aGraphic = pVDev->GetBitmap( Point(), aSize );
                            }
                            else
                                aGraphic = rGraphic.GetBitmapEx();
                        }
                        else
                            aGraphic = rGraphic.GetBitmapEx();
                    }

                    // mirror?
                    if( ( nFlags & XOutFlags::MirrorHorz ) || ( nFlags & XOutFlags::MirrorVert ) )
                    {
                        BmpMirrorFlags nBmpMirrorFlags = BmpMirrorFlags::NONE;
                        if( nFlags & XOutFlags::MirrorHorz )
                          nBmpMirrorFlags |= BmpMirrorFlags::Horizontal;
                        if( nFlags & XOutFlags::MirrorVert )
                          nBmpMirrorFlags |= BmpMirrorFlags::Vertical;
                        aGraphic = MirrorGraphic( aGraphic, nBmpMirrorFlags );
                    }

                    if( ( GRFILTER_FORMAT_NOTFOUND != nFilter ) && ( aGraphic.GetType() != GraphicType::NONE ) )
                    {
                        if( !(nFlags & XOutFlags::DontAddExtension) )
                            aURL.setExtension( aExt );
                        rFileName = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
                        nErr = ExportGraphic( aGraphic, aURL, rFilter, nFilter, pFilterData );
                    }
                }
            }

            return nErr;
        }
        else
        {
            return ERRCODE_NONE;
        }
    }

    bool XOutBitmap::GraphicToBase64(const Graphic& rGraphic, OUString& rOUString, bool bAddPrefix,
                                     ConvertDataFormat aTargetFormat)
    {
        SvMemoryStream aOStm;
        GfxLink aLink = rGraphic.GetGfxLink();

        if (aTargetFormat == ConvertDataFormat::Unknown)
        {
            switch (aLink.GetType())
            {
                case GfxLinkType::NativeJpg:
                    aTargetFormat = ConvertDataFormat::JPG;
                    break;
                case GfxLinkType::NativePng:
                    aTargetFormat = ConvertDataFormat::PNG;
                    break;
                case GfxLinkType::NativeSvg:
                    aTargetFormat = ConvertDataFormat::SVG;
                    break;
                default:
                    // save everything else (including gif) into png
                    aTargetFormat = ConvertDataFormat::PNG;
                    break;
            }
        }

        ErrCode nErr = GraphicConverter::Export(aOStm,rGraphic,aTargetFormat);
        if ( nErr )
        {
            SAL_WARN("svx", "XOutBitmap::GraphicToBase64() invalid Graphic? error: " << nErr );
            return false;
        }
        aOStm.Seek(STREAM_SEEK_TO_END);
        css::uno::Sequence<sal_Int8> aOStmSeq( static_cast<sal_Int8 const *>(aOStm.GetData()),aOStm.Tell() );
        OUStringBuffer aStrBuffer;
        ::comphelper::Base64::encode(aStrBuffer,aOStmSeq);
        rOUString = aStrBuffer.makeStringAndClear();

        if (bAddPrefix)
        {
            OUString aMimeType
                = comphelper::GraphicMimeTypeHelper::GetMimeTypeForConvertDataFormat(aTargetFormat);
            rOUString = aMimeType + ";base64," + rOUString;
        }

        return true;
    }

    ErrCode XOutBitmap::ExportGraphic( const Graphic& rGraphic, const INetURLObject& rURL,
                                      GraphicFilter& rFilter, const sal_uInt16 nFormat,
                                      const css::uno::Sequence< css::beans::PropertyValue >* pFilterData )
    {
        DBG_ASSERT( rURL.GetProtocol() != INetProtocol::NotValid, "XOutBitmap::ExportGraphic(...): invalid URL" );

        SfxMedium   aMedium( rURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::WRITE | StreamMode::SHARE_DENYNONE | StreamMode::TRUNC );
        SvStream*   pOStm = aMedium.GetOutStream();
        ErrCode     nRet = ERRCODE_GRFILTER_IOERROR;

        if( pOStm )
        {
            pGrfFilter = &rFilter;

            nRet = rFilter.ExportGraphic( rGraphic, rURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), *pOStm, nFormat, pFilterData );

            pGrfFilter = nullptr;
            aMedium.Commit();

            if( aMedium.GetError() && ( ERRCODE_NONE == nRet  ) )
                nRet = ERRCODE_GRFILTER_IOERROR;
        }

        return nRet;
    }

    Bitmap XOutBitmap::DetectEdges( const Bitmap& rBmp, const sal_uInt8 cThreshold )
    {
        const Size  aSize( rBmp.GetSizePixel() );
        Bitmap      aRetBmp;

        if( ( aSize.Width() > 2 ) && ( aSize.Height() > 2 ) )
        {
            Bitmap aWorkBmp( rBmp );

            if( aWorkBmp.Convert( BmpConversion::N8BitGreys ) )
            {
                bool bRet = false;

                ScopedVclPtr<VirtualDevice> pVirDev(VclPtr<VirtualDevice>::Create());
                pVirDev->SetOutputSizePixel(aSize);
                Bitmap::ScopedReadAccess pReadAcc(aWorkBmp);

                if( pReadAcc )
                {
                    const long          nWidth = aSize.Width();
                    const long          nWidth2 = nWidth - 2;
                    const long          nHeight = aSize.Height();
                    const long          nHeight2 = nHeight - 2;
                    const long          lThres2 = static_cast<long>(cThreshold) * cThreshold;
                    long                nSum1;
                    long                nSum2;
                    long                lGray;

                    // initialize border with white pixels
                    pVirDev->SetLineColor( COL_WHITE );
                    pVirDev->DrawLine( Point(), Point( nWidth - 1, 0L ) );
                    pVirDev->DrawLine( Point( nWidth - 1, 0L ), Point( nWidth - 1, nHeight - 1 ) );
                    pVirDev->DrawLine( Point( nWidth - 1, nHeight - 1 ), Point( 0L, nHeight - 1 ) );
                    pVirDev->DrawLine( Point( 0, nHeight - 1 ), Point() );

                    for( long nY = 0, nY1 = 1, nY2 = 2; nY < nHeight2; nY++, nY1++, nY2++ )
                    {
                        Scanline pScanlineRead = pReadAcc->GetScanline( nY );
                        Scanline pScanlineRead1 = pReadAcc->GetScanline( nY1 );
                        Scanline pScanlineRead2 = pReadAcc->GetScanline( nY2 );
                        for( long nX = 0, nXDst = 1, nXTmp; nX < nWidth2; nX++, nXDst++ )
                        {
                            nXTmp = nX;

                            nSum1 = -( nSum2 = lGray = pReadAcc->GetIndexFromData( pScanlineRead, nXTmp++ ) );
                            nSum2 += static_cast<long>(pReadAcc->GetIndexFromData( pScanlineRead, nXTmp++ )) << 1;
                            nSum1 += ( lGray = pReadAcc->GetIndexFromData( pScanlineRead, nXTmp ) );
                            nSum2 += lGray;

                            nSum1 += static_cast<long>(pReadAcc->GetIndexFromData( pScanlineRead1, nXTmp )) << 1;
                            nSum1 -= static_cast<long>(pReadAcc->GetIndexFromData( pScanlineRead1, nXTmp -= 2 )) << 1;

                            nSum1 += ( lGray = -static_cast<long>(pReadAcc->GetIndexFromData( pScanlineRead2, nXTmp++ )) );
                            nSum2 += lGray;
                            nSum2 -= static_cast<long>(pReadAcc->GetIndexFromData( pScanlineRead2, nXTmp++ )) << 1;
                            nSum1 += ( lGray = static_cast<long>(pReadAcc->GetIndexFromData( pScanlineRead2, nXTmp )) );
                            nSum2 -= lGray;

                            if( ( nSum1 * nSum1 + nSum2 * nSum2 ) < lThres2 )
                                pVirDev->DrawPixel( Point(nXDst, nY), COL_WHITE );
                            else
                                pVirDev->DrawPixel( Point(nXDst, nY), COL_BLACK );
                        }
                    }

                    bRet = true;
                }

                pReadAcc.reset();

                if( bRet )
                    aRetBmp = pVirDev->GetBitmap(Point(0,0), aSize);
            }
        }

        if( !aRetBmp )
            aRetBmp = rBmp;
        else
        {
            aRetBmp.SetPrefMapMode( rBmp.GetPrefMapMode() );
            aRetBmp.SetPrefSize( rBmp.GetPrefSize() );
        }

        return aRetBmp;
    }

    tools::Polygon XOutBitmap::GetContour( const Bitmap& rBmp, const XOutFlags nFlags,
                                            const tools::Rectangle* pWorkRectPixel )
    {
        const sal_uInt8 cEdgeDetectThreshold = 128;
        Bitmap      aWorkBmp;
        tools::Polygon aRetPoly;
        tools::Rectangle   aWorkRect( Point(), rBmp.GetSizePixel() );

        if( pWorkRectPixel )
            aWorkRect.Intersection( *pWorkRectPixel );

        aWorkRect.Justify();

        if( ( aWorkRect.GetWidth() > 4 ) && ( aWorkRect.GetHeight() > 4 ) )
        {
            // if the flag is set, we need to detect edges
            if( nFlags & XOutFlags::ContourEdgeDetect )
                aWorkBmp = DetectEdges( rBmp, cEdgeDetectThreshold );
            else
                aWorkBmp = rBmp;

            BitmapReadAccess* pAcc = aWorkBmp.AcquireReadAccess();

            const long nWidth = pAcc ? pAcc->Width() : 0;
            const long nHeight = pAcc ? pAcc->Height() : 0;

            if (pAcc && nWidth && nHeight)
            {
                const Size&         rPrefSize = aWorkBmp.GetPrefSize();
                const double        fFactorX = static_cast<double>(rPrefSize.Width()) / nWidth;
                const double        fFactorY = static_cast<double>(rPrefSize.Height()) / nHeight;
                const long          nStartX1 = aWorkRect.Left() + 1;
                const long          nEndX1 = aWorkRect.Right();
                const long          nStartX2 = nEndX1 - 1;
                const long          nStartY1 = aWorkRect.Top() + 1;
                const long          nEndY1 = aWorkRect.Bottom();
                const long          nStartY2 = nEndY1 - 1;
                std::unique_ptr<Point[]> pPoints1;
                std::unique_ptr<Point[]> pPoints2;
                long                nX, nY;
                sal_uInt16              nPolyPos = 0;
                const BitmapColor   aBlack = pAcc->GetBestMatchingColor( COL_BLACK );

                if( nFlags & XOutFlags::ContourVert )
                {
                    pPoints1.reset(new Point[ nWidth ]);
                    pPoints2.reset(new Point[ nWidth ]);

                    for( nX = nStartX1; nX < nEndX1; nX++ )
                    {
                        nY = nStartY1;

                        // scan row from left to right
                        while( nY < nEndY1 )
                        {
                            Scanline pScanline = pAcc->GetScanline( nY );
                            if( aBlack == pAcc->GetPixelFromData( pScanline, nX ) )
                            {
                                pPoints1[ nPolyPos ] = Point( nX, nY );
                                nY = nStartY2;

                                // this loop always breaks eventually as there is at least one pixel
                                while( true )
                                {
                                    // coverity[copy_paste_error : FALSE] - this is correct nX, not nY
                                    if( aBlack == pAcc->GetPixelFromData( pScanline, nX ) )
                                    {
                                        pPoints2[ nPolyPos ] = Point( nX, nY );
                                        break;
                                    }

                                    nY--;
                                }

                                nPolyPos++;
                                break;
                            }

                            nY++;
                        }
                    }
                }
                else
                {
                    pPoints1.reset(new Point[ nHeight ]);
                    pPoints2.reset(new Point[ nHeight ]);

                    for ( nY = nStartY1; nY < nEndY1; nY++ )
                    {
                        nX = nStartX1;
                        Scanline pScanline = pAcc->GetScanline( nY );

                        // scan row from left to right
                        while( nX < nEndX1 )
                        {
                            if( aBlack == pAcc->GetPixelFromData( pScanline, nX ) )
                            {
                                pPoints1[ nPolyPos ] = Point( nX, nY );
                                nX = nStartX2;

                                // this loop always breaks eventually as there is at least one pixel
                                while( true )
                                {
                                    if( aBlack == pAcc->GetPixelFromData( pScanline, nX ) )
                                    {
                                        pPoints2[ nPolyPos ] = Point( nX, nY );
                                        break;
                                    }

                                    nX--;
                                }

                                nPolyPos++;
                                break;
                            }

                            nX++;
                        }
                    }
                }

                const sal_uInt16 nNewSize1 = nPolyPos << 1;

                aRetPoly = tools::Polygon( nPolyPos, pPoints1.get() );
                aRetPoly.SetSize( nNewSize1 + 1 );
                aRetPoly[ nNewSize1 ] = aRetPoly[ 0 ];

                for( sal_uInt16 j = nPolyPos; nPolyPos < nNewSize1; )
                    aRetPoly[ nPolyPos++ ] = pPoints2[ --j ];

                if( ( fFactorX != 0. ) && ( fFactorY != 0. ) )
                    aRetPoly.Scale( fFactorX, fFactorY );
            }

            Bitmap::ReleaseAccess(pAcc);
        }

        return aRetPoly;
    }

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

↑ V560 A part of conditional expression is always true: ((sal_uInt16(0xFFFF)) != nFilter).