// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//
#include <e32def.h>
#include "regionextend.h"
//Only call this if certain neither rectangle is empty.
//Region rectangles are always non-empty
inline /*static*/
TRegionExtend::TOverlapFlags TestDifferenceNotEmpty(const TRect& aThis,const TRect& aThat)
{
struct SubAdd
{
inline static TRegionExtend::TOverlapFlags Conv(TInt delta)
{ //returns sub, exact or add for delta of each edge pair
//neg --> +1 //pos --> +2 //zero ==> 0
return TRegionExtend::TOverlapFlags(
((delta>>31)&1) +(((-delta)>>30)&2) );
}
};
//could use SubAdd for this if...
if ( aThis.iTl.iX>=aThat.iBr.iX
|| aThis.iTl.iY>=aThat.iBr.iY
|| aThat.iTl.iX>=aThis.iBr.iX
|| aThat.iTl.iY>=aThis.iBr.iY
)
return TRegionExtend::TOverlapFlags(TRegionExtend::EDiffers|TRegionExtend::ENoIntersect);
TInt subAdd=( SubAdd::Conv(aThis.iTl.iX-aThat.iTl.iX)
| SubAdd::Conv(aThis.iTl.iY-aThat.iTl.iY)
| SubAdd::Conv(aThat.iBr.iX-aThis.iBr.iX)
| SubAdd::Conv(aThat.iBr.iY-aThis.iBr.iY)
);
return
TRegionExtend::TOverlapFlags(subAdd);
}
/** Calc total area of a list of non-intersecting rectangles (ie a region)
* @param aThisRects the array of rectangles
* @param aThisCount the number in the array
*/
inline TUint RectListArea(const TRect* aThisRects,TInt aThisCount)
{
TInt thisArea=0;
for (TInt stepThis=aThisCount-1;stepThis>=0;stepThis--)
{
TSize size=aThisRects[stepThis].Size();
__ASSERT_DEBUG((size.iWidth|size.iHeight)<32768,User::Panic(_L("TRegionExtend"),4003));
thisArea+=size.iWidth*size.iHeight;
}
return thisArea;
}
/** Returns the result code flag based on the calculated intersection of two areas
* The intersection is always less than or equal to this and that.
* @param aThisArea Start area
* @param aIntersectArea Intersection
* @param aThatArea Final area
* @return
* - EDiffers if both this and that differ from intersect
* - EExact if both this and that are same as intersect
* - ESub if only that is same as intersect, so this is bigger
* - EAdd if only this is same as intersect, so that is bigger
*
**/
inline TRegionExtend::TOverlapFlags AreaDiffResults(TUint aThisArea,TUint aIntersectArea,TUint aThatArea)
{
if (aThisArea>aIntersectArea)
if (aThatArea>aIntersectArea)
return TRegionExtend::EDiffers;
else
return TRegionExtend::ESub;
else
if (aThatArea>aIntersectArea)
return TRegionExtend::EAdd;
else
return TRegionExtend::EExact;
}
/** Calculates the intersection area of one rectangle with an array of non intersecting rectangles
* It is presumed that the caller will loop through all the cells of a target region,
* repeating this call for a source region, so we can add an extra optimisation
* to avoid revisiting any source rectangles that have been consumed completely in later passes.
* The simplest test is that the source cell is wholy inside the target rect.
* The simplest record is a bit field, but that only works up to 32 elements, then will not optimise further elements.
* @param aThisCount num elements in rect array
* @param aThisRects array of rectangles
* @param aThatRect intersecting rectangle
* @param aOptimiseThisBits record of elements of rect aray that have been fully consumed
* @return total intersection area
**/
inline TUint TestDifferenceRegionInnerLoop(TInt aThisCount,const TRect* aThisRects,TRect& aThatRect,TUint& aOptimiseThisBits)
{
TUint intersectArea=0;
for (TInt stepThis=aThisCount-1,bit=1;stepThis>=0;stepThis--,bit<<=1)
{
if (!(aOptimiseThisBits&bit))
{
const TRect& thisRect=aThisRects[stepThis];
TRegionExtend::TOverlapFlags flags=TestDifferenceNotEmpty(thisRect,aThatRect);
if (!(flags&TRegionExtend::ENoIntersect))
{
if (!(flags&TRegionExtend::EAdd))
{ //Skip rest of inner loop if a containing rect is found
TSize size=aThatRect.Size();
intersectArea+=size.iWidth*size.iHeight;
if (!(flags&TRegionExtend::ESub)) //equal rects...
{ //skip this cell for rest of outer loop if a contains rect is found
aOptimiseThisBits|=bit;
}
break; //this cell contains the target rect so don't bother checking any more
}
else
if (!(flags&TRegionExtend::ESub))
{ //skip this cell for rest of outer loop if a contains rect is found
aOptimiseThisBits|=bit;
TSize size=thisRect.Size();
intersectArea+=size.iWidth*size.iHeight;
}
else
{
TRect intersect=thisRect;
intersect.Intersection(aThatRect);
TSize size=intersect.Size();
intersectArea+=size.iWidth*size.iHeight;
}
}
}
}
return intersectArea;
}
/** Avoids the use of a temp region by performing area calc on the fly.
* If both regions are empty then EOverlapNoIntersect only is returned.
* @param aThat target region
* @return flags from TOverlapFlags enumeration
* - EExact =0 if rgions are exactly identical
* - ESub Flagged if some rectangles are removed converting current region to that target
* - EAdd Flagged if some rectangles are added converting current region to that target
* - ENoIntersect if there is no common region between the current region and that target
* - EErrorRegion One of the regions is signalling CheckError()
**/
TRegionExtend::TOverlapFlags TRegionExtend::TestDifference(const TRegion& aThat)const
{
TInt intersectArea=0;
TInt thisArea=0;
TInt thatArea=0;
const TRect* thisRects=RectangleList();
const TRect* thatRects=aThat.RectangleList();
TInt thatCount=aThat.Count();
TInt thisCount=Count();
if (CheckError()||aThat.CheckError())
return EErrorRegion;
if (thisCount==0)
if (thatCount==0)
return ENoIntersect;
else
return TOverlapFlags(ENoIntersect|EAdd);
//both regions are populated. How big is the intersection?
//The following optimisation bit is that
//if any rect is fully contained by a rect in the opposite region
//then further compares against that rect are skipped. For this, inner loop is skipped immediately
//Can track the first 32 rects of aThat. The remainder won't benefit from the optimisation
TUint optimiseThisBits=0;
for (TInt stepThat=thatCount-1;stepThat>=0;stepThat--)
{
TRect thatRect=thatRects[stepThat];
intersectArea+=TestDifferenceRegionInnerLoop(thisCount,thisRects,thatRect,optimiseThisBits);
}
if (intersectArea==0)
if (thatCount==0)
return TOverlapFlags(ENoIntersect|ESub);
else
return TOverlapFlags(ENoIntersect|EAdd|ESub);
thatArea=RectListArea(thatRects,thatCount);
thisArea=RectListArea(thisRects,thisCount);
return AreaDiffResults( thisArea, intersectArea, thatArea );
}
/** Avoids the use of a temp region by performing area calc on the fly.
* This version further optimises the process by avoiding the client having to re-origin either region.
* If both regions are empty then EOverlapNoIntersect only is returned.
* @param aThat target region
* @return flags from TOverlapFlags enumeration
* - EExact =0 if rgions are exactly identical
* - ESub Flagged if some rectangles are removed converting current region to that target
* - EAdd Flagged if some rectangles are added converting current region to that target
* - ENoIntersect if there is no common region between the current region and that target
* - EErrorRegion One of the regions is signalling CheckError()
**/
TRegionExtend::TOverlapFlags TRegionExtend::TestDifference(const TRegion& aThat,TPoint aOffsetToThat)const
{
TInt intersectArea=0;
TInt thisArea=0;
TInt thatArea=0;
const TRect* thisRects=RectangleList();
const TRect* thatRects=aThat.RectangleList();
TInt thatCount=aThat.Count();
TInt thisCount=Count();
if (CheckError()||aThat.CheckError())
return EErrorRegion;
if (thisCount==0)
if (thatCount==0)
return ENoIntersect;
else
return TOverlapFlags(ENoIntersect|EAdd);
//both regions are populated. How big is the intersection?
//The following optimisation bit is that
//if any rect is fully contained by a rect in the opposite region
//then further compares against that rect are skipped. For this, inner loop is skipped immediately
//Can track the first 32 rects of aThat. The remainder won't benefit from the optimisation
TUint optimiseThisBits=0;
for (TInt stepThat=thatCount-1;stepThat>=0;stepThat--)
{
TRect thatRect=thatRects[stepThat];
thatRect.Move(-aOffsetToThat.iX,-aOffsetToThat.iY); //this line is the only difference, but the next lump has a lot of parameters...
intersectArea+=TestDifferenceRegionInnerLoop(thisCount,thisRects,thatRect,optimiseThisBits);
}
if (intersectArea==0)
if (thatCount==0)
return TOverlapFlags(ENoIntersect|ESub);
else
return TOverlapFlags(ENoIntersect|EAdd|ESub);
thatArea=RectListArea(thatRects,thatCount);
thisArea=RectListArea(thisRects,thisCount);
return AreaDiffResults( thisArea, intersectArea, thatArea );
}
/** This returns the same comparrison flags between two rectangles.
* Note that a zero return means exact intersection...
* Intended as an internal method, but there doesn't seem to be a useful public alternative.
* @param aThis source rectangle
* @param aThat target rectangle
* @return flags from TOverlapFlags enumeration
* - EExact =0 if rgions are exactly identical
* - ESub Flagged if some rectangles are removed converting this source rectangle to that target
* - EAdd Flagged if some rectangles are added converting this source rectangle to that target
* - ENoIntersect if there is no common region between this source rectangle and that target
**/
TRegionExtend::TOverlapFlags TRegionExtend::TestDifference(const TRect& aThis,const TRect& aThat)
{
if (aThis.IsEmpty())
if (aThat.IsEmpty())
return ENoIntersect;
else
return TOverlapFlags(EAdd|ENoIntersect);
else
if (aThat.IsEmpty())
return TOverlapFlags(ESub|ENoIntersect);
return TestDifferenceNotEmpty(aThis,aThat);
}
/** Returns total area of the region
* @return total area
**/
TUint TRegionExtend::Area()const
{
return RectListArea(RectangleList(),Count());
}
/** Avoids the use of a temp region by performing area calc on the fly.
* If both are empty then EOverlapNoIntersect only is returned.
* @param aThat target rectangle
* @return flags from TOverlapFlags enumeration
* - EExact =0 if region exactly fills rectangle
* - ESub Flagged if some rectangles are removed converting current region to that target
* - EAdd Flagged if some rectangles are added converting current region to that target
* - ENoIntersect if there is no common region between the current region and that target
* - EErrorRegion One of the region is signalling CheckError()
**/
TRegionExtend::TOverlapFlags TRegionExtend::TestDifference(const TRect& aThat)const
{
TInt intersectArea=0;
const TRect* thisRects=RectangleList();
TInt thisCount=Count();
if (aThat.IsEmpty())
if (thisCount==0)
return ENoIntersect;
else
return TOverlapFlags(ENoIntersect|ESub);
if (CheckError())
return EErrorRegion;
TInt output=ENoIntersect;
for (TInt stepThis=thisCount-1,bit=1;stepThis>=0;stepThis--,bit+=bit)
{
TOverlapFlags flags=TestDifferenceNotEmpty(thisRects[stepThis],aThat);
if (!(flags&ENoIntersect))
{
if (!(flags&EAdd)) //the target rect does not add anything to this region element
{ //Skip rest of inner loop if a containing rect is found
if ((flags&ESub)||thisCount>1)
return ESub; //the region element is bigger or there are more region elements
else
return EExact;
}
else
{
TRect intersect=thisRects[stepThis];
intersect.Intersection(aThat);
TSize size=intersect.Size();
__ASSERT_DEBUG((size.iWidth|size.iHeight)<32768,User::Panic(_L("TRegionExtend"),1003));
intersectArea+=size.iWidth*size.iHeight;
}
output&=~ENoIntersect;
}
output|=(flags&ESub);
}
if (intersectArea==0)
{
return TOverlapFlags(output|EAdd);
}
TSize size=aThat.Size();
__ASSERT_DEBUG((size.iWidth|size.iHeight)<32768,User::Panic(_L("TRegionExtend"),2003));
TInt thatArea=size.iWidth*size.iHeight;
if (thatArea>intersectArea)
return TOverlapFlags(output|EAdd);
else
return TOverlapFlags(output);
}