Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  region.cxx   Sprache: C

 
/* -*- 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 <tools/vcompat.hxx>
#include <tools/stream.hxx>
#include <osl/diagnose.h>
#include <sal/log.hxx>
#include <vcl/canvastools.hxx>
#include <vcl/region.hxx>
#include <regionband.hxx>

#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/polygon/b2dpolygonclipper.hxx>
#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
#include <basegfx/range/b2drange.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <tools/poly.hxx>
#include <comphelper/configuration.hxx>

namespace
{
    /** Return <TRUE/> when the given polygon is rectilinear and oriented so that
        all sides are either horizontal or vertical.
    */

    bool ImplIsPolygonRectilinear (const tools::PolyPolygon& rPolyPoly)
    {
        // Iterate over all polygons.
        const sal_uInt16 nPolyCount = rPolyPoly.Count();
        for (sal_uInt16 nPoly = 0; nPoly < nPolyCount; ++nPoly)
        {
            const tools::Polygon&  aPoly = rPolyPoly.GetObject(nPoly);

            // Iterate over all edges of the current polygon.
            const sal_uInt16 nSize = aPoly.GetSize();

            if (nSize < 2)
                continue;
            Point aPoint (aPoly.GetPoint(0));
            const Point aLastPoint (aPoint);
            for (sal_uInt16 nPoint = 1; nPoint < nSize; ++nPoint)
            {
                const Point aNextPoint (aPoly.GetPoint(nPoint));
                // When there is at least one edge that is neither vertical nor
                // horizontal then the entire polygon is not rectilinear (and
                // oriented along primary axes.)
                if (aPoint.X() != aNextPoint.X() && aPoint.Y() != aNextPoint.Y())
                    return false;

                aPoint = aNextPoint;
            }
            // Compare closing edge.
            if (aLastPoint.X() != aPoint.X() && aLastPoint.Y() != aPoint.Y())
                return false;
        }
        return true;
    }

    /** Convert a rectilinear polygon (that is oriented along the primary axes)
        to a list of bands.  For this special form of polygon we can use an
        optimization that prevents the creation of one band per y value.
        However, it still is possible that some temporary bands are created that
        later can be optimized away.
        @param rPolyPolygon
            A set of zero, one, or more polygons, nested or not, that are
            converted into a list of bands.
        @return
            A new RegionBand object is returned that contains the bands that
            represent the given poly-polygon.
    */

    std::shared_ptr<RegionBand> ImplRectilinearPolygonToBands(const tools::PolyPolygon&&nbsp;rPolyPoly)
    {
        OSL_ASSERT(ImplIsPolygonRectilinear (rPolyPoly));

        // Create a new RegionBand object as container of the bands.
        std::shared_ptr<RegionBand> pRegionBand( std::make_shared<RegionBand>() );
        tools::Long nLineId = 0;

        // Iterate over all polygons.
        const sal_uInt16 nPolyCount = rPolyPoly.Count();
        for (sal_uInt16 nPoly = 0; nPoly < nPolyCount; ++nPoly)
        {
            const tools::Polygon&  aPoly = rPolyPoly.GetObject(nPoly);

            // Iterate over all edges of the current polygon.
            const sal_uInt16 nSize = aPoly.GetSize();
            if (nSize < 2)
                continue;
            // Avoid fetching every point twice (each point is the start point
            // of one and the end point of another edge.)
            Point aStart (aPoly.GetPoint(0));
            Point aEnd;
            for (sal_uInt16 nPoint = 1; nPoint <= nSize; ++nPoint, aStart=aEnd)
            {
                // We take the implicit closing edge into account by mapping
                // index nSize to 0.
                aEnd = aPoly.GetPoint(nPoint%nSize);
                if (aStart.Y() == aEnd.Y())
                {
                    // Horizontal lines are ignored.
                    continue;
                }

                // At this point the line has to be vertical.
                OSL_ASSERT(aStart.X() == aEnd.X());

                // Sort y-coordinates to simplify the algorithm and store the
                // direction separately.  The direction is calculated as it is
                // in other places (but seems to be the wrong way.)
                const tools::Long nTop (::std::min(aStart.Y(), aEnd.Y()));
                const tools::Long nBottom (::std::max(aStart.Y(), aEnd.Y()));
                const LineType eLineType (aStart.Y() > aEnd.Y() ? LineType::Descending : LineType::Ascending);

                // Make sure that the current line is covered by bands.
                pRegionBand->ImplAddMissingBands(nTop,nBottom);

                // Find top-most band that may contain nTop.
                ImplRegionBand* pBand = pRegionBand->ImplGetFirstRegionBand();
                while (pBand!=nullptr && pBand->mnYBottom < nTop)
                    pBand = pBand->mpNextBand;
                ImplRegionBand* pTopBand = pBand;
                // If necessary split the band at nTop so that nTop is contained
                // in the lower band.
                if (pBand!=nullptr
                       // Prevent the current band from becoming 0 pixel high
                    && pBand->mnYTop<nTop
                       // this allows the lowest pixel of the band to be split off
                    && pBand->mnYBottom>=nTop
                       // do not split a band that is just one pixel high
                    && pBand->mnYTop<pBand->mnYBottom-1)
                {
                    // Split the top band.
                    pTopBand = pBand->SplitBand(nTop);
                }

                // Advance to band that may contain nBottom.
                while (pBand!=nullptr && pBand->mnYBottom < nBottom)
                    pBand = pBand->mpNextBand;
                // The lowest band may have to be split at nBottom so that
                // nBottom itself remains in the upper band.
                if (pBand!=nullptr
                       // allow the current band becoming 1 pixel high
                    && pBand->mnYTop<=nBottom
                       // prevent splitting off a band that is 0 pixel high
                    && pBand->mnYBottom>nBottom
                       // do not split a band that is just one pixel high
                    && pBand->mnYTop<pBand->mnYBottom-1)
                {
                    // Split the bottom band.
                    pBand->SplitBand(nBottom+1);
                }

                // Note that we remember the top band (in pTopBand) but not the
                // bottom band.  The later can be determined by comparing y
                // coordinates.

                // Add the x-value as point to all bands in the nTop->nBottom range.
                for (pBand=pTopBand; pBand!=nullptr&&pBand->mnYTop<=nBottom; pBand=pBand->mpNextBand)
                    pBand->InsertPoint(aStart.X(), nLineId++, true, eLineType);
            }
        }

        return pRegionBand;
    }

    /** Convert a general polygon (one for which ImplIsPolygonRectilinear()
        returns <FALSE/>) to bands.
    */

    std::shared_ptr<RegionBand> ImplGeneralPolygonToBands(const tools::PolyPolygon& rPolyPoly, const tools::Rectangle& rPolygonBoundingBox)
    {
        tools::Long nLineID = 0;

        // initialisation and creation of Bands
        std::shared_ptr<RegionBand> pRegionBand( std::make_shared<RegionBand>() );
        pRegionBand->CreateBandRange(rPolygonBoundingBox.Top(), rPolygonBoundingBox.Bottom());

        // insert polygons
        const sal_uInt16 nPolyCount = rPolyPoly.Count();

        for ( sal_uInt16 nPoly = 0; nPoly < nPolyCount; nPoly++ )
        {
            // get reference to current polygon
            const tools::Polygon&  aPoly = rPolyPoly.GetObject( nPoly );
            const sal_uInt16    nSize = aPoly.GetSize();

            // not enough points ( <= 2 )? -> nothing to do!
            if ( nSize <= 2 )
                continue;

            // band the polygon
            for ( sal_uInt16 nPoint = 1; nPoint < nSize; nPoint++ )
            {
                pRegionBand->InsertLine( aPoly.GetPoint(nPoint-1), aPoly.GetPoint(nPoint), nLineID++ );
            }

            // close polygon with line from first point to last point, if necessary
            const Point rLastPoint = aPoly.GetPoint(nSize-1);
            const Point rFirstPoint = aPoly.GetPoint(0);

            if ( rLastPoint != rFirstPoint )
            {
                pRegionBand->InsertLine( rLastPoint, rFirstPoint, nLineID++ );
            }
        }

        return pRegionBand;
    }
// end of anonymous namespace

namespace vcl {

bool vcl::Region::IsEmpty() const
{
    return !mbIsNull && !mpB2DPolyPolygon && !mpPolyPolygon && !mpRegionBand;
}


static std::shared_ptr<RegionBand> ImplCreateRegionBandFromPolyPolygon(const tools::PolyPolygon& rPolyPolygon)
{
    std::shared_ptr<RegionBand> pRetval;

    if(rPolyPolygon.Count())
    {
        // ensure to subdivide when bezier segments are used, it's going to
        // be expanded to rectangles
        tools::PolyPolygon aPolyPolygon;

        rPolyPolygon.AdaptiveSubdivide(aPolyPolygon);

        if(aPolyPolygon.Count())
        {
            const tools::Rectangle aRect(aPolyPolygon.GetBoundRect());

            if(!aRect.IsEmpty())
            {
                if(ImplIsPolygonRectilinear(aPolyPolygon))
                {
                    // For rectilinear polygons there is an optimized band conversion.
                    pRetval = ImplRectilinearPolygonToBands(aPolyPolygon);
                }
                else
                {
                    pRetval = ImplGeneralPolygonToBands(aPolyPolygon, aRect);
                }

                // Convert points into seps.
                if(pRetval)
                {
                    pRetval->processPoints();

                    // Optimize list of bands.  Adjacent bands with identical lists
                    // of seps are joined.
                    if(!pRetval->OptimizeBandList())
                    {
                        pRetval.reset();
                    }
                }
            }
        }
    }

    return pRetval;
}

tools::PolyPolygon vcl::Region::ImplCreatePolyPolygonFromRegionBand() const
{
    tools::PolyPolygon aRetval;

    if(getRegionBand())
    {
        RectangleVector aRectangles;
        GetRegionRectangles(aRectangles);

        for (auto const& rectangle : aRectangles)
        {
            aRetval.Insert( tools::Polygon(rectangle) );
        }
    }
    else
    {
        OSL_ENSURE(false"Called with no local RegionBand (!)");
    }

    return aRetval;
}

basegfx::B2DPolyPolygon vcl::Region::ImplCreateB2DPolyPolygonFromRegionBand() const
{
    tools::PolyPolygon aPoly(ImplCreatePolyPolygonFromRegionBand());

    return aPoly.getB2DPolyPolygon();
}

Region::Region(bool bIsNull)
:   mbIsNull(bIsNull)
{
}

Region::Region(const tools::Rectangle& rRect)
:   mbIsNull(false)
{
    if (!rRect.IsEmpty())
        mpRegionBand = std::make_shared<RegionBand>(rRect);
}

Region::Region(const tools::Polygon& rPolygon)
:   mbIsNull(false)
{

    if(rPolygon.GetSize())
    {
        ImplCreatePolyPolyRegion(tools::PolyPolygon(rPolygon));
    }
}

Region::Region(const tools::PolyPolygon& rPolyPoly)
:   mbIsNull(false)
{

    if(rPolyPoly.Count())
    {
        ImplCreatePolyPolyRegion(rPolyPoly);
    }
}

Region::Region(const basegfx::B2DPolyPolygon& rPolyPoly)
:   mbIsNull(false)
{

    if(rPolyPoly.count())
    {
        ImplCreatePolyPolyRegion(rPolyPoly);
    }
}

Region::Region(const vcl::Region&) = default;

Region::Region(vcl::Region&& rRegion) noexcept
:   mpB2DPolyPolygon(std::move(rRegion.mpB2DPolyPolygon)),
    mpPolyPolygon(std::move(rRegion.mpPolyPolygon)),
    mpRegionBand(std::move(rRegion.mpRegionBand)),
    mbIsNull(rRegion.mbIsNull)
{
    rRegion.mbIsNull = true;
}

Region::~Region() = default;

void vcl::Region::ImplCreatePolyPolyRegion( const tools::PolyPolygon& rPolyPoly )
{
    const sal_uInt16 nPolyCount = rPolyPoly.Count();

    if(!nPolyCount)
        return;

    // polypolygon empty? -> empty region
    const tools::Rectangle aRect(rPolyPoly.GetBoundRect());

    if(aRect.IsEmpty())
        return;

    // width OR height == 1 ? => Rectangular region
    if((1 == aRect.GetWidth()) || (1 == aRect.GetHeight()) || rPolyPoly.IsRect())
    {
        mpRegionBand = std::make_shared<RegionBand>(aRect);
    }
    else
    {
        mpPolyPolygon = rPolyPoly;
    }

    mbIsNull = false;
}

void vcl::Region::ImplCreatePolyPolyRegion( const basegfx::B2DPolyPolygon& rPolyPoly )
{
    if(rPolyPoly.count() && !rPolyPoly.getB2DRange().isEmpty())
    {
        mpB2DPolyPolygon = rPolyPoly;
        mbIsNull = false;
    }
}

void vcl::Region::Move( tools::Long nHorzMove, tools::Long nVertMove )
{
    if(IsNull() || IsEmpty())
    {
        // empty or null need no move
        return;
    }

    if(!nHorzMove && !nVertMove)
    {
        // no move defined
        return;
    }

    if(getB2DPolyPolygon())
    {
        basegfx::B2DPolyPolygon aPoly(*getB2DPolyPolygon());

        aPoly.translate(nHorzMove, nVertMove);
        if (aPoly.count())
            mpB2DPolyPolygon = aPoly;
        else
            mpB2DPolyPolygon.reset();
        mpPolyPolygon.reset();
        mpRegionBand.reset();
    }
    else if(getPolyPolygon())
    {
        tools::PolyPolygon aPoly(*getPolyPolygon());

        aPoly.Move(nHorzMove, nVertMove);
        mpB2DPolyPolygon.reset();
        if (aPoly.Count())
            mpPolyPolygon = aPoly;
        else
            mpPolyPolygon.reset();
        mpRegionBand.reset();
    }
    else if(getRegionBand())
    {
        std::shared_ptr<RegionBand> pNew = std::make_shared<RegionBand>(*getRegionBand());

        pNew->Move(nHorzMove, nVertMove);
        mpB2DPolyPolygon.reset();
        mpPolyPolygon.reset();
        mpRegionBand = std::move(pNew);
    }
    else
    {
        OSL_ENSURE(false"Region::Move error: impossible combination (!)");
    }
}

void vcl::Region::Scale( double fScaleX, double fScaleY )
{
    if(IsNull() || IsEmpty())
    {
        // empty or null need no scale
        return;
    }

    if(basegfx::fTools::equalZero(fScaleX) && basegfx::fTools::equalZero(fScaleY))
    {
        // no scale defined
        return;
    }

    if(getB2DPolyPolygon())
    {
        basegfx::B2DPolyPolygon aPoly(*getB2DPolyPolygon());

        aPoly.transform(basegfx::utils::createScaleB2DHomMatrix(fScaleX, fScaleY));
        if (aPoly.count())
            mpB2DPolyPolygon = aPoly;
        else
            mpB2DPolyPolygon.reset();
        mpPolyPolygon.reset();
        mpRegionBand.reset();
    }
    else if(getPolyPolygon())
    {
        tools::PolyPolygon aPoly(*getPolyPolygon());

        aPoly.Scale(fScaleX, fScaleY);
        mpB2DPolyPolygon.reset();
        if (aPoly.Count())
            mpPolyPolygon = aPoly;
        else
            mpPolyPolygon.reset();
        mpRegionBand.reset();
    }
    else if(getRegionBand())
    {
        std::shared_ptr<RegionBand> pNew = std::make_shared<RegionBand>(*getRegionBand());

        pNew->Scale(fScaleX, fScaleY);
        mpB2DPolyPolygon.reset();
        mpPolyPolygon.reset();
        mpRegionBand = std::move(pNew);
    }
    else
    {
        OSL_ENSURE(false"Region::Scale error: impossible combination (!)");
    }
}

void vcl::Region::Unionconst tools::Rectangle& rRect )
{
    if(rRect.IsEmpty())
    {
        // empty rectangle will not expand the existing union, nothing to do
        return;
    }

    if(IsEmpty())
    {
        // no local data, the union will be equal to source. Create using rectangle
        *this = rRect;
        return;
    }

    if(HasPolyPolygonOrB2DPolyPolygon())
    {
        // get this B2DPolyPolygon, solve on polygon base
        basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());

        aThisPolyPoly = basegfx::utils::prepareForPolygonOperation(aThisPolyPoly);

        if(!aThisPolyPoly.count())
        {
            // no local polygon, use the rectangle as new region
            *this = rRect;
        }
        else
        {
            // get the other B2DPolyPolygon and use logical Or-Operation
            const basegfx::B2DPolygon aRectPoly(
                basegfx::utils::createPolygonFromRect(
                        vcl::unotools::b2DRectangleFromRectangle(rRect)));
            const basegfx::B2DPolyPolygon aClip(
                basegfx::utils::solvePolygonOperationOr(
                    aThisPolyPoly,
                    basegfx::B2DPolyPolygon(aRectPoly)));
            *this = vcl::Region(aClip);
        }

        return;
    }

    // only region band mode possibility left here or null/empty
    const RegionBand* pCurrent = getRegionBand();

    if(!pCurrent)
    {
        // no region band, create using the rectangle
        *this = rRect;
        return;
    }

    std::shared_ptr<RegionBand> pNew = std::make_shared<RegionBand>(*pCurrent);

    // get justified rectangle
    const tools::Long nLeft(std::min(rRect.Left(), rRect.Right()));
    const tools::Long nTop(std::min(rRect.Top(), rRect.Bottom()));
    const tools::Long nRight(std::max(rRect.Left(), rRect.Right()));
    const tools::Long nBottom(std::max(rRect.Top(), rRect.Bottom()));

    // insert bands if the boundaries are not already in the list
    pNew->InsertBands(nTop, nBottom);

    // process union
    pNew->Union(nLeft, nTop, nRight, nBottom);

    // cleanup
    if(!pNew->OptimizeBandList())
    {
        pNew.reset();
    }

    mpRegionBand = std::move(pNew);
}

void vcl::Region::Intersect( const tools::Rectangle& rRect )
{
    if ( rRect.IsEmpty() )
    {
        // empty rectangle will create empty region
        SetEmpty();
        return;
    }

    if(IsNull())
    {
        // null region (everything) intersect with rect will give rect
        *this = rRect;
        return;
    }

    if(IsEmpty())
    {
        // no content, cannot get more empty
        return;
    }

    if(HasPolyPolygonOrB2DPolyPolygon())
    {
        // if polygon data prefer double precision, the other will be lost (if buffered)
        if(getB2DPolyPolygon())
        {
            const basegfx::B2DPolyPolygon aPoly(
                basegfx::utils::clipPolyPolygonOnRange(
                    *getB2DPolyPolygon(),
                    basegfx::B2DRange(
                        rRect.Left(),
                        rRect.Top(),
                        rRect.Right() + 1,
                        rRect.Bottom() + 1),
                    true,
                    false));

            if (aPoly.count())
                mpB2DPolyPolygon = aPoly;
            else
                mpB2DPolyPolygon.reset();
            mpPolyPolygon.reset();
            mpRegionBand.reset();
        }
        else // if(getPolyPolygon())
        {
            tools::PolyPolygon aPoly(*getPolyPolygon());

            // use the PolyPolygon::Clip method for rectangles, this is
            // fairly simple (does not even use GPC) and saves us from
            // unnecessary banding
            aPoly.Clip(rRect);

            mpB2DPolyPolygon.reset();
            if (aPoly.Count())
                mpPolyPolygon = aPoly;
            else
                mpPolyPolygon.reset();
            mpRegionBand.reset();
        }

        return;
    }

    // only region band mode possibility left here or null/empty
    const RegionBand* pCurrent = getRegionBand();

    if(!pCurrent)
    {
        // region is empty -> nothing to do!
        return;
    }

    std::shared_ptr<RegionBand> pNew( std::make_shared<RegionBand>(*pCurrent));

    // get justified rectangle
    const tools::Long nLeft(std::min(rRect.Left(), rRect.Right()));
    const tools::Long nTop(std::min(rRect.Top(), rRect.Bottom()));
    const tools::Long nRight(std::max(rRect.Left(), rRect.Right()));
    const tools::Long nBottom(std::max(rRect.Top(), rRect.Bottom()));

    // insert bands if the boundaries are not already in the list
    pNew->InsertBands(nTop, nBottom);

    // process intersect
    pNew->Intersect(nLeft, nTop, nRight, nBottom);

    // cleanup
    if(!pNew->OptimizeBandList())
    {
        pNew.reset();
    }

    mpRegionBand = std::move(pNew);
}

void vcl::Region::Exclude( const tools::Rectangle& rRect )
{
    if ( rRect.IsEmpty() )
    {
        // excluding nothing will do no change
        return;
    }

    if(IsEmpty())
    {
        // cannot exclude from empty, done
        return;
    }

    if(IsNull())
    {
        // error; cannot exclude from null region since this is not representable
        // in the data
        OSL_ENSURE(false"Region::Exclude error: Cannot exclude from null region (!)");
        return;
    }

    if( HasPolyPolygonOrB2DPolyPolygon() )
    {
        // get this B2DPolyPolygon
        basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());

        aThisPolyPoly = basegfx::utils::prepareForPolygonOperation(aThisPolyPoly);

        if(!aThisPolyPoly.count())
        {
            // when local polygon is empty, nothing can be excluded
            return;
        }

        // get the other B2DPolyPolygon
        const basegfx::B2DPolygon aRectPoly(
            basegfx::utils::createPolygonFromRect(
                vcl::unotools::b2DRectangleFromRectangle(rRect)));
        const basegfx::B2DPolyPolygon aOtherPolyPoly(aRectPoly);
        const basegfx::B2DPolyPolygon aClip = basegfx::utils::solvePolygonOperationDiff(aThisPolyPoly, aOtherPolyPoly);

        *this = vcl::Region(aClip);

        return;
    }

    // only region band mode possibility left here or null/empty
    if(!mpRegionBand)
    {
        // empty? -> done!
        return;
    }

    std::shared_ptr<RegionBand>& pNew = mpRegionBand;
    // only make a copy if someone else is also using it
    if (pNew.use_count() > 1)
        pNew = std::make_shared<RegionBand>(*pNew);

    // get justified rectangle
    const tools::Long nLeft(std::min(rRect.Left(), rRect.Right()));
    const tools::Long nTop(std::min(rRect.Top(), rRect.Bottom()));
    const tools::Long nRight(std::max(rRect.Left(), rRect.Right()));
    const tools::Long nBottom(std::max(rRect.Top(), rRect.Bottom()));

    // insert bands if the boundaries are not already in the list
    pNew->InsertBands(nTop, nBottom);

    // process exclude
    pNew->Exclude(nLeft, nTop, nRight, nBottom);

    // cleanup
    if(!pNew->OptimizeBandList())
        pNew.reset();
}

void vcl::Region::XOrconst tools::Rectangle& rRect )
{
    if ( rRect.IsEmpty() )
    {
        // empty rectangle will not change local content
        return;
    }

    if(IsEmpty())
    {
        // rRect will be the xored-form (local off, rect on)
        *this = rRect;
        return;
    }

    if(IsNull())
    {
        // error; cannot exclude from null region since this is not representable
        // in the data
        OSL_ENSURE(false"Region::XOr error: Cannot XOr with null region (!)");
        return;
    }

    if( HasPolyPolygonOrB2DPolyPolygon() )
    {
        // get this B2DPolyPolygon
        basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());

        aThisPolyPoly = basegfx::utils::prepareForPolygonOperation( aThisPolyPoly );

        if(!aThisPolyPoly.count())
        {
            // no local content, XOr will be equal to rectangle
            *this = rRect;
            return;
        }

        // get the other B2DPolyPolygon
        const basegfx::B2DPolygon aRectPoly(
            basegfx::utils::createPolygonFromRect(
                vcl::unotools::b2DRectangleFromRectangle(rRect)));
        const basegfx::B2DPolyPolygon aOtherPolyPoly(aRectPoly);
        const basegfx::B2DPolyPolygon aClip = basegfx::utils::solvePolygonOperationXor(aThisPolyPoly, aOtherPolyPoly);

        *this = vcl::Region(aClip);

        return;
    }

    // only region band mode possibility left here or null/empty
    const RegionBand* pCurrent = getRegionBand();

    if(!pCurrent)
    {
        // rRect will be the xored-form (local off, rect on)
        *this = rRect;
        return;
    }

    // only region band mode possibility left here or null/empty
    std::shared_ptr<RegionBand> pNew( std::make_shared<RegionBand>(*getRegionBand()));

    // get justified rectangle
    const tools::Long nLeft(std::min(rRect.Left(), rRect.Right()));
    const tools::Long nTop(std::min(rRect.Top(), rRect.Bottom()));
    const tools::Long nRight(std::max(rRect.Left(), rRect.Right()));
    const tools::Long nBottom(std::max(rRect.Top(), rRect.Bottom()));

    // insert bands if the boundaries are not already in the list
    pNew->InsertBands(nTop, nBottom);

    // process xor
    pNew->XOr(nLeft, nTop, nRight, nBottom);

    // cleanup
    if(!pNew->OptimizeBandList())
    {
        pNew.reset();
    }

    mpRegionBand = std::move(pNew);
}

void vcl::Region::Unionconst vcl::Region& rRegion )
{
    if(rRegion.IsEmpty())
    {
        // no extension at all
        return;
    }

    if(rRegion.IsNull())
    {
        // extending with null region -> null region
        *this = vcl::Region(true);
        return;
    }

    if(IsEmpty())
    {
        // local is empty, union will give source region
        *this = rRegion;
        return;
    }

    if(IsNull())
    {
        // already fully expanded (is null region), cannot be extended
        return;
    }

    if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
    {
        // get this B2DPolyPolygon
        basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());

        aThisPolyPoly = basegfx::utils::prepareForPolygonOperation(aThisPolyPoly);

        if(!aThisPolyPoly.count())
        {
            // when no local content, union will be equal to rRegion
            *this = rRegion;
            return;
        }

        // get the other B2DPolyPolygon
        basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
        aOtherPolyPoly = basegfx::utils::prepareForPolygonOperation(aOtherPolyPoly);

        // use logical OR operation
        basegfx::B2DPolyPolygon aClip(basegfx::utils::solvePolygonOperationOr(aThisPolyPoly, aOtherPolyPoly));

        *this = vcl::Region( aClip );
        return;
    }

    // only region band mode possibility left here or null/empty
    const RegionBand* pCurrent = getRegionBand();

    if(!pCurrent)
    {
        // local is empty, union will give source region
        *this = rRegion;
        return;
    }

    const RegionBand* pSource = rRegion.getRegionBand();

    if(!pSource)
    {
        // no extension at all
        return;
    }

    // prepare source and target
    std::shared_ptr<RegionBand> pNew( std::make_shared<RegionBand>(*pCurrent));

    // union with source
    pNew->Union(*pSource);

    // cleanup
    if(!pNew->OptimizeBandList())
    {
        pNew.reset();
    }

    mpRegionBand = std::move(pNew);
}

void vcl::Region::Intersect( const vcl::Region& rRegion )
{
    // same instance data? -> nothing to do!
    if(getB2DPolyPolygon() && getB2DPolyPolygon() == rRegion.getB2DPolyPolygon())
    {
        return;
    }

    if(getPolyPolygon() && getPolyPolygon() == rRegion.getPolyPolygon())
    {
        return;
    }

    if(getRegionBand() && getRegionBand() == rRegion.getRegionBand())
    {
        return;
    }

    if(rRegion.IsNull())
    {
        // source region is null-region, intersect will not change local region
        return;
    }

    if(IsNull())
    {
        // when local region is null-region, intersect will be equal to source
        *this = rRegion;
        return;
    }

    if(rRegion.IsEmpty())
    {
        // source region is empty, intersection will always be empty
        SetEmpty();
        return;
    }

    if(IsEmpty())
    {
        // local region is empty, cannot get more empty than that. Nothing to do
        return;
    }

    if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
    {
        // get this B2DPolyPolygon
        basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());

        if(!aThisPolyPoly.count())
        {
            // local region is empty, cannot get more empty than that. Nothing to do
            return;
        }

        // get the other B2DPolyPolygon
        basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());

        if(!aOtherPolyPoly.count())
        {
            // source region is empty, intersection will always be empty
            SetEmpty();
            return;
        }

        static size_t gPointLimit = !comphelper::IsFuzzing() ? SAL_MAX_SIZE : 8192;
        size_t nPointLimit(gPointLimit);
        const basegfx::B2DPolyPolygon aClip(
            basegfx::utils::clipPolyPolygonOnPolyPolygon(
                aOtherPolyPoly,
                aThisPolyPoly,
                true,
                false,
                &nPointLimit));
        *this = vcl::Region( aClip );
        return;
    }

    // only region band mode possibility left here or null/empty
    const RegionBand* pCurrent = getRegionBand();

    if(!pCurrent)
    {
        // local region is empty, cannot get more empty than that. Nothing to do
        return;
    }

    const RegionBand* pSource = rRegion.getRegionBand();

    if(!pSource)
    {
        // source region is empty, intersection will always be empty
        SetEmpty();
        return;
    }

    // both RegionBands exist and are not empty
    if(pCurrent->getRectangleCount() + 2 < pSource->getRectangleCount())
    {
        // when we have less rectangles, turn around the call
        vcl::Region aTempRegion = rRegion;
        aTempRegion.Intersect( *this );
        *this = std::move(aTempRegion);
    }
    else
    {
        // prepare new regionBand
        std::shared_ptr<RegionBand> pNew( std::make_shared<RegionBand>(*pCurrent));

        // intersect with source
        pNew->Intersect(*pSource);

        // cleanup
        if(!pNew->OptimizeBandList())
        {
            pNew.reset();
        }

        mpRegionBand = std::move(pNew);
    }
}

void vcl::Region::Exclude( const vcl::Region& rRegion )
{
    if ( rRegion.IsEmpty() )
    {
        // excluding nothing will do no change
        return;
    }

    if ( rRegion.IsNull() )
    {
        // excluding everything will create empty region
        SetEmpty();
        return;
    }

    if(IsEmpty())
    {
        // cannot exclude from empty, done
        return;
    }

    if(IsNull())
    {
        // error; cannot exclude from null region since this is not representable
        // in the data
        OSL_ENSURE(false"Region::Exclude error: Cannot exclude from null region (!)");
        return;
    }

    if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
    {
        // get this B2DPolyPolygon
        basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());

        if(!aThisPolyPoly.count())
        {
            // cannot exclude from empty, done
            return;
        }

        aThisPolyPoly = basegfx::utils::prepareForPolygonOperation( aThisPolyPoly );

        // get the other B2DPolyPolygon
        basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
        aOtherPolyPoly = basegfx::utils::prepareForPolygonOperation( aOtherPolyPoly );

        basegfx::B2DPolyPolygon aClip = basegfx::utils::solvePolygonOperationDiff( aThisPolyPoly, aOtherPolyPoly );
        *this = vcl::Region( aClip );
        return;
    }

    // only region band mode possibility left here or null/empty
    const RegionBand* pCurrent = getRegionBand();

    if(!pCurrent)
    {
        // cannot exclude from empty, done
        return;
    }

    const RegionBand* pSource = rRegion.getRegionBand();

    if(!pSource)
    {
        // excluding nothing will do no change
        return;
    }

    // prepare source and target
    std::shared_ptr<RegionBand> pNew( std::make_shared<RegionBand>(*pCurrent));

    // union with source
    const bool bSuccess(pNew->Exclude(*pSource));

    // cleanup
    if(!bSuccess)
    {
        pNew.reset();
    }

    mpRegionBand = std::move(pNew);
}

bool vcl::Region::XOrconst vcl::Region& rRegion )
{
    if ( rRegion.IsEmpty() )
    {
        // empty region will not change local content
        return true;
    }

    if ( rRegion.IsNull() )
    {
        // error; cannot exclude null region from local since this is not representable
        // in the data
        OSL_ENSURE(false"Region::XOr error: Cannot XOr with null region (!)");
        return true;
    }

    if(IsEmpty())
    {
        // rRect will be the xored-form (local off, rect on)
        *this = rRegion;
        return true;
    }

    if(IsNull())
    {
        // error: cannot exclude from null region since this is not representable
        // in the data
        OSL_ENSURE(false"Region::XOr error: Cannot XOr with null region (!)");
        return false;
    }

    if( rRegion.HasPolyPolygonOrB2DPolyPolygon() || HasPolyPolygonOrB2DPolyPolygon() )
    {
        // get this B2DPolyPolygon
        basegfx::B2DPolyPolygon aThisPolyPoly(GetAsB2DPolyPolygon());

        if(!aThisPolyPoly.count())
        {
            // rRect will be the xored-form (local off, rect on)
            *this = rRegion;
            return true;
        }

        aThisPolyPoly = basegfx::utils::prepareForPolygonOperation( aThisPolyPoly );

        // get the other B2DPolyPolygon
        basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
        aOtherPolyPoly = basegfx::utils::prepareForPolygonOperation( aOtherPolyPoly );

        basegfx::B2DPolyPolygon aClip = basegfx::utils::solvePolygonOperationXor( aThisPolyPoly, aOtherPolyPoly );
        *this = vcl::Region( aClip );
        return true;
    }

    // only region band mode possibility left here or null/empty
    const RegionBand* pCurrent = getRegionBand();

    if(!pCurrent)
    {
        // rRect will be the xored-form (local off, rect on)
        *this = rRegion;
        return true;
    }

    const RegionBand* pSource = rRegion.getRegionBand();

    if(!pSource)
    {
        // empty region will not change local content
        return true;
    }

    // prepare source and target
    std::shared_ptr<RegionBand> pNew( std::make_shared<RegionBand>(*pCurrent));

    // union with source
    pNew->XOr(*pSource);

    // cleanup
    if(!pNew->OptimizeBandList())
    {
        pNew.reset();
    }

    mpRegionBand = std::move(pNew);

    return true;
}

tools::Rectangle vcl::Region::GetBoundRect() const
{
    if(IsEmpty())
    {
        // no internal data? -> region is empty!
        return tools::Rectangle();
    }

    if(IsNull())
    {
        // error; null region has no BoundRect
        // OSL_ENSURE(false, "Region::GetBoundRect error: null region has unlimited bound rect, not representable (!)");
        return tools::Rectangle();
    }

    // prefer double precision source
    if(getB2DPolyPolygon())
    {
        const basegfx::B2DRange aRange(basegfx::utils::getRange(*getB2DPolyPolygon()));

        if(aRange.isEmpty())
        {
            // emulate PolyPolygon::GetBoundRect() when empty polygon
            return tools::Rectangle();
        }
        else
        {
            // #i122149# corrected rounding, no need for ceil() and floor() here
            return tools::Rectangle(
                basegfx::fround<tools::Long>(aRange.getMinX()), basegfx::fround<tools::Long>(aRange.getMinY()),
                basegfx::fround<tools::Long>(aRange.getMaxX()), basegfx::fround<tools::Long>(aRange.getMaxY()));
        }
    }

    if(getPolyPolygon())
    {
        return getPolyPolygon()->GetBoundRect();
    }

    if(getRegionBand())
    {
        return getRegionBand()->GetBoundRect();
    }

    return tools::Rectangle();
}

tools::PolyPolygon vcl::Region::GetAsPolyPolygon() const
{
    if(getPolyPolygon())
    {
        return *getPolyPolygon();
    }

    if(getB2DPolyPolygon())
    {
        // the polygon needs to be converted, buffer the down conversion
        const tools::PolyPolygon aPolyPolgon(*getB2DPolyPolygon());
        const_cast< vcl::Region* >(this)->mpPolyPolygon = aPolyPolgon;

        return *getPolyPolygon();
    }

    if(getRegionBand())
    {
        // the BandRegion needs to be converted, buffer the conversion
        const tools::PolyPolygon aPolyPolgon(ImplCreatePolyPolygonFromRegionBand());
        const_cast< vcl::Region* >(this)->mpPolyPolygon = aPolyPolgon;

        return *getPolyPolygon();
    }

    return tools::PolyPolygon();
}

basegfx::B2DPolyPolygon vcl::Region::GetAsB2DPolyPolygon() const
{
    if(getB2DPolyPolygon())
    {
        return *getB2DPolyPolygon();
    }

    if(getPolyPolygon())
    {
        // the polygon needs to be converted, buffer the up conversion. This will be preferred from now.
        const basegfx::B2DPolyPolygon aB2DPolyPolygon(getPolyPolygon()->getB2DPolyPolygon());
        const_cast< vcl::Region* >(this)->mpB2DPolyPolygon = aB2DPolyPolygon;

        return *getB2DPolyPolygon();
    }

    if(getRegionBand())
    {
        // the BandRegion needs to be converted, buffer the conversion
        const basegfx::B2DPolyPolygon aB2DPolyPolygon(ImplCreateB2DPolyPolygonFromRegionBand());
        const_cast< vcl::Region* >(this)->mpB2DPolyPolygon = aB2DPolyPolygon;

        return *getB2DPolyPolygon();
    }

    return basegfx::B2DPolyPolygon();
}

const RegionBand* vcl::Region::GetAsRegionBand() const
{
    if(!getRegionBand())
    {
        if(getB2DPolyPolygon())
        {
            // convert B2DPolyPolygon to RegionBand, buffer it and return it
            const_cast< vcl::Region* >(this)->mpRegionBand = ImplCreateRegionBandFromPolyPolygon(tools::PolyPolygon(*getB2DPolyPolygon()));
        }
        else if(getPolyPolygon())
        {
            // convert B2DPolyPolygon to RegionBand, buffer it and return it
            const_cast< vcl::Region* >(this)->mpRegionBand = ImplCreateRegionBandFromPolyPolygon(*getPolyPolygon());
        }
    }

    return getRegionBand();
}

bool vcl::Region::Contains( const Point& rPoint ) const
{
    if(IsEmpty())
    {
        // no point can be in empty region
        return false;
    }

    if(IsNull())
    {
        // all points are inside null-region
        return true;
    }

    // Too expensive (?)
    //if(mpImplRegion->getRegionPolyPoly())
    //{
    //  return mpImplRegion->getRegionPolyPoly()->Contains( rPoint );
    //}

    // ensure RegionBand existence
    const RegionBand* pRegionBand = GetAsRegionBand();

    if(pRegionBand)
    {
        return pRegionBand->Contains(rPoint);
    }

    return false;
}

bool vcl::Region::Overlaps( const tools::Rectangle& rRect ) const
{
    if(IsEmpty())
    {
        // nothing can be over something empty
        return false;
    }

    if(IsNull())
    {
        // everything is over null region
        return true;
    }

    // Can we optimize this ??? - is used in StarDraw for brushes pointers
    // Why we have no IsOver for Regions ???
    // create region from rectangle and intersect own region
    vcl::Region aRegion(rRect);
    aRegion.Intersect( *this );

    // rectangle is over if include is not empty
    return !aRegion.IsEmpty();
}

bool vcl::Region::IsRectangle() const
{
    if( IsEmpty() || IsNull() )
        return false;

    if( getB2DPolyPolygon() )
        return basegfx::utils::isRectangle( *getB2DPolyPolygon() );

    if( getPolyPolygon() )
        return getPolyPolygon()->IsRect();

    if( getRegionBand() )
        return (getRegionBand()->getRectangleCount() == 1);

    return false;
}

void vcl::Region::SetNull()
{
    // reset all content
    mpB2DPolyPolygon.reset();
    mpPolyPolygon.reset();
    mpRegionBand.reset();
    mbIsNull = true;
}

void vcl::Region::SetEmpty()
{
    // reset all content
    mpB2DPolyPolygon.reset();
    mpPolyPolygon.reset();
    mpRegionBand.reset();
    mbIsNull = false;
}

Region& vcl::Region::operator=( const vcl::Region& ) = default;

Region& vcl::Region::operator=( vcl::Region&& rRegion ) noexcept
{
    mpB2DPolyPolygon = std::move(rRegion.mpB2DPolyPolygon);
    mpPolyPolygon = std::move(rRegion.mpPolyPolygon);
    mpRegionBand = std::move(rRegion.mpRegionBand);
    mbIsNull = rRegion.mbIsNull;
    rRegion.mbIsNull = true;

    return *this;
}

Region& vcl::Region::operator=( const tools::Rectangle& rRect )
{
    mpB2DPolyPolygon.reset();
    mpPolyPolygon.reset();
    if (!rRect.IsEmpty())
        mpRegionBand = std::make_shared<RegionBand>(rRect);
    else
        mpRegionBand.reset();
    mbIsNull = false;

    return *this;
}

bool vcl::Region::operator==( const vcl::Region& rRegion ) const
{
    if(IsNull() && rRegion.IsNull())
    {
        // both are null region
        return true;
    }

    if(IsEmpty() && rRegion.IsEmpty())
    {
        // both are empty
        return true;
    }

    if(getB2DPolyPolygon() && getB2DPolyPolygon() == rRegion.getB2DPolyPolygon())
    {
        // same instance data? -> equal
        return true;
    }

    if(getPolyPolygon() && getPolyPolygon() == rRegion.getPolyPolygon())
    {
        // same instance data? -> equal
        return true;
    }

    if(getRegionBand() && getRegionBand() == rRegion.getRegionBand())
    {
        // same instance data? -> equal
        return true;
    }

    if(IsNull() || IsEmpty())
    {
        return false;
    }

    if(rRegion.IsNull() || rRegion.IsEmpty())
    {
        return false;
    }

    if(rRegion.getB2DPolyPolygon() || getB2DPolyPolygon())
    {
        // one of both has a B2DPolyPolygon based region, ensure both have it
        // by evtl. conversion
        GetAsB2DPolyPolygon();
        rRegion.GetAsB2DPolyPolygon();

        return *rRegion.getB2DPolyPolygon() == *getB2DPolyPolygon();
    }

    if(rRegion.getPolyPolygon() || getPolyPolygon())
    {
        // one of both has a B2DPolyPolygon based region, ensure both have it
        // by evtl. conversion
        GetAsPolyPolygon();
        rRegion.GetAsPolyPolygon();

        return *rRegion.getPolyPolygon() == *getPolyPolygon();
    }

    // both are not empty or null (see above) and if content supported polygon
    // data the comparison is already done. Only both on RegionBand base can be left,
    // but better check
    if(rRegion.getRegionBand() && getRegionBand())
    {
        return *rRegion.getRegionBand() == *getRegionBand();
    }

    // should not happen, but better deny equality
    return false;
}

SvStream& ReadRegion(SvStream& rIStrm, vcl::Region& rRegion)
{
    VersionCompatRead aCompat(rIStrm);
    sal_uInt16 nVersion(0);
    sal_uInt16 nTmp16(0);

    // clear region to be loaded
    rRegion.SetEmpty();

    // get version of streamed region
    rIStrm.ReadUInt16( nVersion );

    // get type of region
    rIStrm.ReadUInt16( nTmp16 );

    enum RegionType { REGION_NULL, REGION_EMPTY, REGION_RECTANGLE, REGION_COMPLEX };
    auto eStreamedType = nTmp16;

    switch (eStreamedType)
    {
        case REGION_NULL:
        {
            rRegion.SetNull();
            break;
        }

        case REGION_EMPTY:
        {
            rRegion.SetEmpty();
            break;
        }

        default:
        {
            std::shared_ptr<RegionBand> xNewRegionBand(std::make_shared<RegionBand>());
            bool bSuccess = xNewRegionBand->load(rIStrm);
            rRegion.mpRegionBand = std::move(xNewRegionBand);

            bool bHasPolyPolygon(false);
            if (aCompat.GetVersion() >= 2)
            {
                rIStrm.ReadCharAsBool( bHasPolyPolygon );

                if (bHasPolyPolygon)
                {
                    tools::PolyPolygon aNewPoly;
                    ReadPolyPolygon(rIStrm, aNewPoly);
                    const auto nPolygons = aNewPoly.Count();
                    if (nPolygons > 128)
                    {
                        SAL_WARN("vcl.gdi""suspiciously high no of polygons in clip:" << nPolygons);
                        if (comphelper::IsFuzzing())
                            aNewPoly.Clear();
                    }
                    rRegion.mpPolyPolygon = aNewPoly;
                }
            }

            if (!bSuccess && !bHasPolyPolygon)
            {
                SAL_WARN("vcl.gdi""bad region band:" << bHasPolyPolygon);
                rRegion.SetNull();
            }

            break;
        }
    }

    return rIStrm;
}

SvStream& WriteRegion( SvStream& rOStrm, const vcl::Region& rRegion )
{
    const sal_uInt16 nVersion(2);
    VersionCompatWrite aCompat(rOStrm, nVersion);

    // put version
    rOStrm.WriteUInt16( nVersion );

    // put type
    enum RegionType { REGION_NULL, REGION_EMPTY, REGION_RECTANGLE, REGION_COMPLEX };
    RegionType aRegionType(REGION_COMPLEX);
    bool bEmpty(rRegion.IsEmpty());

    if(!bEmpty && rRegion.getB2DPolyPolygon() && 0 == rRegion.getB2DPolyPolygon()->count())
    {
        OSL_ENSURE(false"Region with empty B2DPolyPolygon, should not be created (!)");
        bEmpty = true;
    }

    if(!bEmpty && rRegion.getPolyPolygon() && 0 == rRegion.getPolyPolygon()->Count())
    {
        OSL_ENSURE(false"Region with empty PolyPolygon, should not be created (!)");
        bEmpty = true;
    }

    if(bEmpty)
    {
        aRegionType = REGION_EMPTY;
    }
    else if(rRegion.IsNull())
    {
        aRegionType = REGION_NULL;
    }
    else if(rRegion.getRegionBand() && rRegion.getRegionBand()->isSingleRectangle())
    {
        aRegionType = REGION_RECTANGLE;
    }

    rOStrm.WriteUInt16( aRegionType );

    // get RegionBand
    const RegionBand* pRegionBand = rRegion.getRegionBand();

    if(pRegionBand)
    {
        pRegionBand->save(rOStrm);
    }
    else
    {
        // for compatibility, write an empty RegionBand (will only write
        // the end marker STREAMENTRY_END, but this *is* needed)
        const RegionBand aRegionBand;

        aRegionBand.save(rOStrm);
    }

    // write polypolygon if available
    const bool bHasPolyPolygon(rRegion.HasPolyPolygonOrB2DPolyPolygon());
    rOStrm.WriteBool( bHasPolyPolygon );

    if(bHasPolyPolygon)
    {
        // #i105373#
        tools::PolyPolygon aNoCurvePolyPolygon;
        rRegion.GetAsPolyPolygon().AdaptiveSubdivide(aNoCurvePolyPolygon);

        WritePolyPolygon( rOStrm, aNoCurvePolyPolygon );
    }

    return rOStrm;
}

void vcl::Region::GetRegionRectangles(RectangleVector& rTarget) const
{
    // clear returnvalues
    rTarget.clear();

    // ensure RegionBand existence
    const RegionBand* pRegionBand = GetAsRegionBand();

    if(pRegionBand)
    {
        pRegionBand->GetRegionRectangles(rTarget);
    }
}

static bool ImplPolygonRectTest( const tools::Polygon& rPoly, tools::Rectangle* pRectOut = nullptr )
{
    bool bIsRect = false;
    const Point* pPoints = rPoly.GetConstPointAry();
    sal_uInt16 nPoints = rPoly.GetSize();

    if( nPoints == 4 || (nPoints == 5 && pPoints[0] == pPoints[4]) )
    {
        tools::Long nX1 = pPoints[0].X(), nX2 = pPoints[2].X(), nY1 = pPoints[0].Y(), nY2 = pPoints[2].Y();

        if( ( (pPoints[1].X() == nX1 && pPoints[3].X() == nX2) && (pPoints[1].Y() == nY2 && pPoints[3].Y() == nY1) )
         || ( (pPoints[1].X() == nX2 && pPoints[3].X() == nX1) && (pPoints[1].Y() == nY1 && pPoints[3].Y() == nY2) ) )
        {
            bIsRect = true;

            if( pRectOut )
            {
                tools::Long nSwap;

                if( nX2 < nX1 )
                {
                    nSwap = nX2;
                    nX2 = nX1;
                    nX1 = nSwap;
                }

                if( nY2 < nY1 )
                {
                    nSwap = nY2;
                    nY2 = nY1;
                    nY1 = nSwap;
                }

                if( nX2 != nX1 )
                {
                    nX2--;
                }

                if( nY2 != nY1 )
                {
                    nY2--;
                }

                pRectOut->SetLeft( nX1 );
                pRectOut->SetRight( nX2 );
                pRectOut->SetTop( nY1 );
                pRectOut->SetBottom( nY2 );
            }
        }
    }

    return bIsRect;
}

vcl::Region vcl::Region::GetRegionFromPolyPolygon( const tools::PolyPolygon& rPolyPoly )
{
    //return vcl::Region( rPolyPoly );

    // check if it's worth extracting the XOr'ing the Rectangles
    // empiricism shows that break even between XOr'ing rectangles separately
    // and ImplCreateRegionBandFromPolyPolygon is at half rectangles/half polygons
    int nPolygonRects = 0, nPolygonPolygons = 0;
    int nPolygons = rPolyPoly.Count();

    forint i = 0; i < nPolygons; i++ )
    {
        const tools::Polygon& rPoly = rPolyPoly[i];

        if( ImplPolygonRectTest( rPoly ) )
        {
            nPolygonRects++;
        }
        else
        {
            nPolygonPolygons++;
        }
    }

    if( nPolygonPolygons > nPolygonRects )
    {
        return vcl::Region( rPolyPoly );
    }

    vcl::Region aResult;
    tools::Rectangle aRect;

    forint i = 0; i < nPolygons; i++ )
    {
        const tools::Polygon& rPoly = rPolyPoly[i];

        if( ImplPolygonRectTest( rPoly, &aRect ) )
        {
            aResult.XOr( aRect );
        }
        else
        {
            aResult.XOr( vcl::Region(rPoly) );
        }
    }

    return aResult;
}

/* namespace vcl */

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

Messung V0.5
C=89 H=98 G=93

¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.20Bemerkung:  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge