/* -*- 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 .
*/
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()) returnfalse;
/** 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>() );
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());
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(rPolyPolygon.Count())
{ // ensure to subdivide when bezier segments are used, it's going to // be expanded to rectangles
tools::PolyPolygon aPolyPolygon;
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();
}
}
}
}
}
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());
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;
}
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));
// 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;
}
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());
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);
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());
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;
}
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);
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;
}
// get the other B2DPolyPolygon
basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
aOtherPolyPoly = basegfx::utils::prepareForPolygonOperation( aOtherPolyPoly );
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 constbool 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 returntrue;
}
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 (!)"); returntrue;
}
if(IsEmpty())
{ // rRect will be the xored-form (local off, rect on)
*this = rRegion; returntrue;
}
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 (!)"); returnfalse;
}
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; returntrue;
}
// get the other B2DPolyPolygon
basegfx::B2DPolyPolygon aOtherPolyPoly(rRegion.GetAsB2DPolyPolygon());
aOtherPolyPoly = basegfx::utils::prepareForPolygonOperation( aOtherPolyPoly );
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();
}
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;
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()));
} elseif(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 returnfalse;
}
if(IsNull())
{ // all points are inside null-region returntrue;
}
bool vcl::Region::Overlaps( const tools::Rectangle& rRect ) const
{ if(IsEmpty())
{ // nothing can be over something empty returnfalse;
}
if(IsNull())
{ // everything is over null region returntrue;
}
// 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();
}
if(rRegion.getB2DPolyPolygon() || getB2DPolyPolygon())
{ // one of both has a B2DPolyPolygon based region, ensure both have it // by evtl. conversion
GetAsB2DPolyPolygon();
rRegion.GetAsB2DPolyPolygon();
if(rRegion.getPolyPolygon() || getPolyPolygon())
{ // one of both has a B2DPolyPolygon based region, ensure both have it // by evtl. conversion
GetAsPolyPolygon();
rRegion.GetAsPolyPolygon();
// 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 returnfalse;
}
bool bHasPolyPolygon(false); if (aCompat.GetVersion() >= 2)
{
rIStrm.ReadCharAsBool( bHasPolyPolygon );
if (bHasPolyPolygon)
{
tools::PolyPolygon aNewPoly;
ReadPolyPolygon(rIStrm, aNewPoly); constauto 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();
}
// 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;
}
// 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 constbool bHasPolyPolygon(rRegion.HasPolyPolygonOrB2DPolyPolygon());
rOStrm.WriteBool( bHasPolyPolygon );
// 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];
¤ 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)
¤
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.