office-gobmx/vcl/source/gdi/region.cxx
Stephan Bergmann fe6cce01c8 Fix loplugin:simplifypointertobool for libstdc++ std::shared_ptr
...where the get member function is defined on a std::__shared_ptr base class,
so loplugin:simplifypointertobool used to miss those until now.  (While e.g.
using libc++ on macOS found those cases.)

366d08f2f6 "new loplugin:simplifypointertobool"
was mistaken in breaking isSmartPointerType(const clang::Type* t) out of
isSmartPointerType(const Expr* e); c874294ad9 "Fix
detection of std::unique_ptr/shared_ptr in loplugin:redundantpointerops" had
introduced that indivisible two-step algorithm on purpose.

The amount of additional hits (on Linux) apparently asked for turning
loplugin:simplifypointertobool into a rewriting plugin.  Which in turn showed
that the naive adivce to just "drop the get()" is not sufficient in places that
are not contextually converted to bool, as those places need to be wrapped in a
bool(...) functional cast now.  If the expression was already wrapped in
parentheses, those could be reused as part of the functional cast, but
implementing that showed that such cases are not yet found at all by the
existing loplugin:simplifypointertobool.  Lets leave that TODO for another
commit.

Besides the changes to compilerplugins/ itself, this change has been generated
fully automatically with the rewriting plugin on Linux.

Change-Id: I83107d6f634fc9ac232986f49044d7017df83e2a
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/94888
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
Tested-by: Jenkins
2020-05-26 22:33:02 +02:00

1778 lines
49 KiB
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>
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& rPolyPoly)
{
OSL_ASSERT(ImplIsPolygonRectilinear (rPolyPoly));
// Create a new RegionBand object as container of the bands.
std::shared_ptr<RegionBand> pRegionBand( std::make_shared<RegionBand>() );
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 long nTop (::std::min(aStart.Y(), aEnd.Y()));
const 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)
{
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)
: mpB2DPolyPolygon(),
mpPolyPolygon(),
mpRegionBand(),
mbIsNull(bIsNull)
{
}
Region::Region(const tools::Rectangle& rRect)
: mpB2DPolyPolygon(),
mpPolyPolygon(),
mpRegionBand(),
mbIsNull(false)
{
mpRegionBand.reset(rRect.IsEmpty() ? nullptr : new RegionBand(rRect));
}
Region::Region(const tools::Polygon& rPolygon)
: mpB2DPolyPolygon(),
mpPolyPolygon(),
mpRegionBand(),
mbIsNull(false)
{
if(rPolygon.GetSize())
{
ImplCreatePolyPolyRegion(rPolygon);
}
}
Region::Region(const tools::PolyPolygon& rPolyPoly)
: mpB2DPolyPolygon(),
mpPolyPolygon(),
mpRegionBand(),
mbIsNull(false)
{
if(rPolyPoly.Count())
{
ImplCreatePolyPolyRegion(rPolyPoly);
}
}
Region::Region(const basegfx::B2DPolyPolygon& rPolyPoly)
: mpB2DPolyPolygon(),
mpPolyPolygon(),
mpRegionBand(),
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)
{
// polypolygon empty? -> empty region
const tools::Rectangle aRect(rPolyPoly.GetBoundRect());
if(!aRect.IsEmpty())
{
// width OR height == 1 ? => Rectangular region
if((1 == aRect.GetWidth()) || (1 == aRect.GetHeight()) || rPolyPoly.IsRect())
{
mpRegionBand = std::make_shared<RegionBand>(aRect);
}
else
{
mpPolyPolygon = std::make_shared<tools::PolyPolygon>(rPolyPoly);
}
mbIsNull = false;
}
}
}
void vcl::Region::ImplCreatePolyPolyRegion( const basegfx::B2DPolyPolygon& rPolyPoly )
{
if(rPolyPoly.count() && !rPolyPoly.getB2DRange().isEmpty())
{
mpB2DPolyPolygon = std::make_shared<basegfx::B2DPolyPolygon>(rPolyPoly);
mbIsNull = false;
}
}
void vcl::Region::Move( long nHorzMove, 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.transform(basegfx::utils::createTranslateB2DHomMatrix(nHorzMove, nVertMove));
mpB2DPolyPolygon.reset(aPoly.count() ? new basegfx::B2DPolyPolygon(aPoly) : nullptr);
mpPolyPolygon.reset();
mpRegionBand.reset();
}
else if(getPolyPolygon())
{
tools::PolyPolygon aPoly(*getPolyPolygon());
aPoly.Move(nHorzMove, nVertMove);
mpB2DPolyPolygon.reset();
mpPolyPolygon.reset(aPoly.Count() ? new tools::PolyPolygon(aPoly) : nullptr);
mpRegionBand.reset();
}
else if(getRegionBand())
{
RegionBand* pNew = new RegionBand(*getRegionBand());
pNew->Move(nHorzMove, nVertMove);
mpB2DPolyPolygon.reset();
mpPolyPolygon.reset();
mpRegionBand.reset(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));
mpB2DPolyPolygon.reset(aPoly.count() ? new basegfx::B2DPolyPolygon(aPoly) : nullptr);
mpPolyPolygon.reset();
mpRegionBand.reset();
}
else if(getPolyPolygon())
{
tools::PolyPolygon aPoly(*getPolyPolygon());
aPoly.Scale(fScaleX, fScaleY);
mpB2DPolyPolygon.reset();
mpPolyPolygon.reset(aPoly.Count() ? new tools::PolyPolygon(aPoly) : nullptr);
mpRegionBand.reset();
}
else if(getRegionBand())
{
RegionBand* pNew = new RegionBand(*getRegionBand());
pNew->Scale(fScaleX, fScaleY);
mpB2DPolyPolygon.reset();
mpPolyPolygon.reset();
mpRegionBand.reset(pNew);
}
else
{
OSL_ENSURE(false, "Region::Scale error: impossible combination (!)");
}
}
void vcl::Region::Union( const 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 long nLeft(std::min(rRect.Left(), rRect.Right()));
const long nTop(std::min(rRect.Top(), rRect.Bottom()));
const long nRight(std::max(rRect.Left(), rRect.Right()));
const 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));
mpB2DPolyPolygon.reset(aPoly.count() ? new basegfx::B2DPolyPolygon(aPoly) : nullptr);
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();
mpPolyPolygon.reset(aPoly.Count() ? new tools::PolyPolygon(aPoly) : nullptr);
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 long nLeft(std::min(rRect.Left(), rRect.Right()));
const long nTop(std::min(rRect.Top(), rRect.Bottom()));
const long nRight(std::max(rRect.Left(), rRect.Right()));
const 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
const RegionBand* pCurrent = getRegionBand();
if(!pCurrent)
{
// empty? -> done!
return;
}
std::shared_ptr<RegionBand> pNew( std::make_shared<RegionBand>(*pCurrent));
// get justified rectangle
const long nLeft(std::min(rRect.Left(), rRect.Right()));
const long nTop(std::min(rRect.Top(), rRect.Bottom()));
const long nRight(std::max(rRect.Left(), rRect.Right()));
const 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();
}
mpRegionBand = std::move(pNew);
}
void vcl::Region::XOr( const 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 long nLeft(std::min(rRect.Left(), rRect.Right()));
const long nTop(std::min(rRect.Top(), rRect.Bottom()));
const long nRight(std::max(rRect.Left(), rRect.Right()));
const 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::Union( const 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;
}
const basegfx::B2DPolyPolygon aClip(
basegfx::utils::clipPolyPolygonOnPolyPolygon(
aOtherPolyPoly,
aThisPolyPoly,
true,
false));
*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 = 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::XOr( const 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(aRange.getMinX()), basegfx::fround(aRange.getMinY()),
basegfx::fround(aRange.getMaxX()), basegfx::fround(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 = std::make_shared<tools::PolyPolygon>(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 = std::make_shared<tools::PolyPolygon>(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 = std::make_shared<basegfx::B2DPolyPolygon>(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 = std::make_shared<basegfx::B2DPolyPolygon>(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::IsInside( 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()->IsInside( rPoint );
//}
// ensure RegionBand existence
const RegionBand* pRegionBand = GetAsRegionBand();
if(pRegionBand)
{
return pRegionBand->IsInside(rPoint);
}
return false;
}
bool vcl::Region::IsOver( 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();
mpRegionBand.reset(rRect.IsEmpty() ? nullptr : new RegionBand(rRect));
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)
{
VersionCompat aCompat(rIStrm, StreamMode::READ);
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 };
RegionType meStreamedType = static_cast<RegionType>(nTmp16);
switch(meStreamedType)
{
case REGION_NULL:
{
rRegion.SetNull();
break;
}
case REGION_EMPTY:
{
rRegion.SetEmpty();
break;
}
default:
{
RegionBand* pNewRegionBand = new RegionBand();
bool bSuccess = pNewRegionBand->load(rIStrm);
rRegion.mpRegionBand.reset(pNewRegionBand);
bool bHasPolyPolygon(false);
if (aCompat.GetVersion() >= 2)
{
rIStrm.ReadCharAsBool( bHasPolyPolygon );
if (bHasPolyPolygon)
{
tools::PolyPolygon* pNewPoly = new tools::PolyPolygon();
ReadPolyPolygon( rIStrm, *pNewPoly );
rRegion.mpPolyPolygon.reset(pNewPoly);
}
}
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);
VersionCompat aCompat(rOStrm, StreamMode::WRITE, 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]) )
{
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 )
{
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();
for( int 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;
for( int 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: */