r=mkaply,achimha@innotek.de sr=roc+moz Implement XP regions - currently only used on OS/2
This commit is contained in:
Родитель
778d75bb49
Коммит
8b23526f96
|
@ -0,0 +1,864 @@
|
|||
/*
|
||||
* The contents of this file are subject to the Mozilla Public
|
||||
* License Version 1.1 (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.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Dainis Jonitis,
|
||||
* <Dainis_Jonitis@swh-t.lv>. Portions created by Dainis Jonitis are
|
||||
* Copyright (C) 2001 Dainis Jonitis. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*/
|
||||
|
||||
#include "prlock.h"
|
||||
#include "nsRegion.h"
|
||||
|
||||
|
||||
// Custom memory allocator for nsRegion::RgnRect structures.
|
||||
// Entries are allocated from global memory pool.
|
||||
// Memory pool can grow in size, but it can't shrink.
|
||||
|
||||
#define INIT_MEM_CHUNK_ENTRIES 100
|
||||
#define INCR_MEM_CHUNK_ENTRIES 100
|
||||
|
||||
class RgnRectMemoryAllocator
|
||||
{
|
||||
nsRegion::RgnRect* mFreeListHead;
|
||||
PRUint32 mFreeEntries;
|
||||
void* mChunkListHead;
|
||||
PRLock* mLock;
|
||||
|
||||
|
||||
void Lock () { PR_Lock (mLock); }
|
||||
void Unlock () { PR_Unlock (mLock); }
|
||||
|
||||
void* AllocChunk (PRUint32 aEntries, void* aNextChunk, nsRegion::RgnRect* aTailDest)
|
||||
{
|
||||
PRUint8* pBuf = new PRUint8 [aEntries * sizeof (nsRegion::RgnRect) + sizeof (void*)];
|
||||
*NS_STATIC_CAST (void**, pBuf) = aNextChunk;
|
||||
nsRegion::RgnRect* pRect = NS_STATIC_CAST (nsRegion::RgnRect*, pBuf + sizeof (void*));
|
||||
|
||||
for (PRUint32 cnt = 0 ; cnt < aEntries - 1 ; cnt++)
|
||||
pRect [cnt].next = &pRect [cnt + 1];
|
||||
|
||||
pRect [aEntries - 1].next = aTailDest;
|
||||
|
||||
return pBuf;
|
||||
}
|
||||
|
||||
void FreeChunk (void* aChunk) { delete [] aChunk; }
|
||||
void* NextChunk (const void* aThisChunk) const { return *NS_STATIC_CAST (void**, aThisChunk); }
|
||||
|
||||
nsRegion::RgnRect* ChunkHead (const void* aThisChunk) const
|
||||
{ return NS_STATIC_CAST (nsRegion::RgnRect*, NS_STATIC_CAST (PRUint8*, aThisChunk) + sizeof (void*)); }
|
||||
|
||||
public:
|
||||
RgnRectMemoryAllocator (PRUint32 aNumOfEntries);
|
||||
~RgnRectMemoryAllocator ();
|
||||
|
||||
inline nsRegion::RgnRect* Alloc ();
|
||||
inline void Free (nsRegion::RgnRect* aRect);
|
||||
};
|
||||
|
||||
|
||||
RgnRectMemoryAllocator::RgnRectMemoryAllocator (PRUint32 aNumOfEntries)
|
||||
{
|
||||
mLock = PR_NewLock ();
|
||||
mChunkListHead = AllocChunk (aNumOfEntries, nsnull, nsnull);
|
||||
mFreeEntries = aNumOfEntries;
|
||||
mFreeListHead = ChunkHead (mChunkListHead);
|
||||
}
|
||||
|
||||
RgnRectMemoryAllocator::~RgnRectMemoryAllocator ()
|
||||
{
|
||||
while (mChunkListHead)
|
||||
{
|
||||
void* tmp = mChunkListHead;
|
||||
mChunkListHead = NextChunk (mChunkListHead);
|
||||
FreeChunk (tmp);
|
||||
}
|
||||
|
||||
PR_DestroyLock (mLock);
|
||||
}
|
||||
|
||||
inline nsRegion::RgnRect* RgnRectMemoryAllocator::Alloc ()
|
||||
{
|
||||
Lock ();
|
||||
|
||||
if (mFreeEntries == 0)
|
||||
{
|
||||
mChunkListHead = AllocChunk (INCR_MEM_CHUNK_ENTRIES, mChunkListHead, mFreeListHead);
|
||||
mFreeEntries = INCR_MEM_CHUNK_ENTRIES;
|
||||
mFreeListHead = ChunkHead (mChunkListHead);
|
||||
}
|
||||
|
||||
nsRegion::RgnRect* tmp = mFreeListHead;
|
||||
mFreeListHead = mFreeListHead->next;
|
||||
mFreeEntries--;
|
||||
Unlock ();
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
inline void RgnRectMemoryAllocator::Free (nsRegion::RgnRect* aRect)
|
||||
{
|
||||
Lock ();
|
||||
mFreeEntries++;
|
||||
aRect->next = mFreeListHead;
|
||||
mFreeListHead = aRect;
|
||||
Unlock ();
|
||||
}
|
||||
|
||||
|
||||
// Global pool for nsRegion::RgnRect allocation
|
||||
static RgnRectMemoryAllocator gRectPool (INIT_MEM_CHUNK_ENTRIES);
|
||||
|
||||
|
||||
void* nsRegion::RgnRect::operator new (size_t)
|
||||
{
|
||||
return gRectPool.Alloc ();
|
||||
}
|
||||
|
||||
void nsRegion::RgnRect::operator delete (void* aRect, size_t)
|
||||
{
|
||||
gRectPool.Free (NS_STATIC_CAST (RgnRect*, aRect));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
nsRegion::nsRegion ()
|
||||
{
|
||||
mRectListHead.prev = mRectListHead.next = &mRectListHead;
|
||||
mRectListHead.width = mRectListHead.height = -100; // This is dummy marker node
|
||||
mCurRect = &mRectListHead;
|
||||
mRectCount = 0;
|
||||
mBoundRect.SetRect (0, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
void nsRegion::Empty ()
|
||||
{
|
||||
mCurRect = mRectListHead.next;
|
||||
|
||||
while (mCurRect != &mRectListHead)
|
||||
{
|
||||
RgnRect* tmp = mCurRect;
|
||||
mCurRect = mCurRect->next;
|
||||
delete tmp;
|
||||
}
|
||||
|
||||
mRectCount = 0;
|
||||
mRectListHead.prev = mRectListHead.next = &mRectListHead;
|
||||
mBoundRect.SetRect (0, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
// Insert node in right place of sorted list
|
||||
// If necessary then bounding rectangle could be updated and rectangle combined
|
||||
// with neighbour rectangles. This is usually done in Optimize ()
|
||||
|
||||
void nsRegion::InsertInPlace (RgnRect* aRect, PRBool aOptimizeOnFly)
|
||||
{
|
||||
if (mRectCount == 0)
|
||||
InsertAfter (aRect, &mRectListHead);
|
||||
else
|
||||
{
|
||||
if (aRect->y > mCurRect->y)
|
||||
{
|
||||
mRectListHead.y = 2147483647;
|
||||
|
||||
while (aRect->y > mCurRect->next->y)
|
||||
mCurRect = mCurRect->next;
|
||||
|
||||
while (aRect->y == mCurRect->next->y && aRect->x > mCurRect->next->x)
|
||||
mCurRect = mCurRect->next;
|
||||
|
||||
InsertAfter (aRect, mCurRect);
|
||||
} else
|
||||
if (aRect->y < mCurRect->y)
|
||||
{
|
||||
mRectListHead.y = -2147483647;
|
||||
|
||||
while (aRect->y < mCurRect->prev->y)
|
||||
mCurRect = mCurRect->prev;
|
||||
|
||||
while (aRect->y == mCurRect->prev->y && aRect->x < mCurRect->prev->x)
|
||||
mCurRect = mCurRect->prev;
|
||||
|
||||
InsertBefore (aRect, mCurRect);
|
||||
} else
|
||||
{
|
||||
if (aRect->x > mCurRect->x)
|
||||
{
|
||||
mRectListHead.y = 2147483647;
|
||||
|
||||
while (aRect->y == mCurRect->next->y && aRect->x > mCurRect->next->x)
|
||||
mCurRect = mCurRect->next;
|
||||
|
||||
InsertAfter (aRect, mCurRect);
|
||||
} else
|
||||
{
|
||||
mRectListHead.y = -2147483647;
|
||||
|
||||
while (aRect->y == mCurRect->prev->y && aRect->x < mCurRect->prev->x)
|
||||
mCurRect = mCurRect->prev;
|
||||
|
||||
InsertBefore (aRect, mCurRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (aOptimizeOnFly)
|
||||
{
|
||||
if (mRectCount == 1)
|
||||
mBoundRect = *mCurRect;
|
||||
else
|
||||
{
|
||||
mBoundRect.UnionRect (mBoundRect, *mCurRect);
|
||||
|
||||
// Check if we can go left or up before starting to combine rectangles
|
||||
if ((mCurRect->y == mCurRect->prev->y && mCurRect->height == mCurRect->prev->height &&
|
||||
mCurRect->x == mCurRect->prev->XMost ()) ||
|
||||
(mCurRect->x == mCurRect->prev->x && mCurRect->width == mCurRect->prev->width &&
|
||||
mCurRect->y == mCurRect->prev->YMost ()) )
|
||||
mCurRect = mCurRect->prev;
|
||||
|
||||
// Try to combine with rectangle on right side
|
||||
while (mCurRect->y == mCurRect->next->y && mCurRect->height == mCurRect->next->height &&
|
||||
mCurRect->XMost () == mCurRect->next->x)
|
||||
{
|
||||
mCurRect->width += mCurRect->next->width;
|
||||
delete Remove (mCurRect->next);
|
||||
}
|
||||
|
||||
// Try to combine with rectangle under this one
|
||||
while (mCurRect->x == mCurRect->next->x && mCurRect->width == mCurRect->next->width &&
|
||||
mCurRect->YMost () == mCurRect->next->y)
|
||||
{
|
||||
mCurRect->height += mCurRect->next->height;
|
||||
delete Remove (mCurRect->next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
nsRegion::RgnRect* nsRegion::Remove (RgnRect* aRect)
|
||||
{
|
||||
aRect->prev->next = aRect->next;
|
||||
aRect->next->prev = aRect->prev;
|
||||
mRectCount--;
|
||||
|
||||
if (mCurRect == aRect)
|
||||
mCurRect = (aRect->next != &mRectListHead) ? aRect->next : aRect->prev;
|
||||
|
||||
return aRect;
|
||||
}
|
||||
|
||||
|
||||
// Try to reduce the number of rectangles in complex region by combining with
|
||||
// surrounding ones on right and bottom sides of each rectangle in list.
|
||||
// Update bounding rectangle
|
||||
|
||||
void nsRegion::Optimize ()
|
||||
{
|
||||
mBoundRect.SetRect (0, 0, 0, 0);
|
||||
RgnRect* pRect = mRectListHead.next;
|
||||
|
||||
while (pRect != &mRectListHead)
|
||||
{
|
||||
// Try to combine with rectangle on right side
|
||||
while (pRect->y == pRect->next->y && pRect->height == pRect->next->height &&
|
||||
pRect->XMost () == pRect->next->x)
|
||||
{
|
||||
pRect->width += pRect->next->width;
|
||||
delete Remove (pRect->next);
|
||||
}
|
||||
|
||||
// Try to combine with rectangle under this one
|
||||
while (pRect->x == pRect->next->x && pRect->width == pRect->next->width &&
|
||||
pRect->YMost () == pRect->next->y)
|
||||
{
|
||||
pRect->height += pRect->next->height;
|
||||
delete Remove (pRect->next);
|
||||
}
|
||||
|
||||
mBoundRect.UnionRect (mBoundRect, *pRect);
|
||||
pRect = pRect->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Merge two non-overlapping regions into one.
|
||||
|
||||
void nsRegion::Merge (const nsRegion& aRgn1, const nsRegion& aRgn2)
|
||||
{
|
||||
if (aRgn1.mRectCount == 0) // Region empty. Result is equal to other region
|
||||
Copy (aRgn2);
|
||||
else
|
||||
if (aRgn2.mRectCount == 0) // Region empty. Result is equal to other region
|
||||
Copy (aRgn1);
|
||||
else
|
||||
{
|
||||
const nsRegion* pCopyRegion, *pInsertRegion;
|
||||
|
||||
// Determine which region contains more rectangles. Copy the larger one
|
||||
if (aRgn1.mRectCount >= aRgn2.mRectCount)
|
||||
{
|
||||
pCopyRegion = &aRgn1;
|
||||
pInsertRegion = &aRgn2;
|
||||
} else
|
||||
{
|
||||
pCopyRegion = &aRgn2;
|
||||
pInsertRegion = &aRgn1;
|
||||
}
|
||||
|
||||
if (pInsertRegion == this) // Do merge in-place
|
||||
pInsertRegion = pCopyRegion;
|
||||
else
|
||||
Copy (*pCopyRegion);
|
||||
|
||||
const RgnRect* pSrcRect = pInsertRegion->mRectListHead.next;
|
||||
|
||||
while (pSrcRect != &pInsertRegion->mRectListHead)
|
||||
{
|
||||
InsertInPlace (new RgnRect (*pSrcRect));
|
||||
|
||||
pSrcRect = pSrcRect->next;
|
||||
}
|
||||
|
||||
Optimize ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Copy (const nsRegion& aRegion)
|
||||
{
|
||||
if (&aRegion == this)
|
||||
return *this;
|
||||
|
||||
if (aRegion.mRectCount == 0)
|
||||
Empty ();
|
||||
else
|
||||
{
|
||||
while (mRectCount < aRegion.mRectCount) // Less rectangles than in source. Add missing ones
|
||||
InsertAfter (new RgnRect, &mRectListHead);
|
||||
|
||||
while (mRectCount > aRegion.mRectCount) // More rectangles than in source. Remove unnecessary
|
||||
delete Remove (mRectListHead.next);
|
||||
|
||||
const RgnRect* pSrc = aRegion.mRectListHead.next;
|
||||
RgnRect* pDest = mRectListHead.next;
|
||||
|
||||
while (pSrc != &aRegion.mRectListHead)
|
||||
{
|
||||
*pDest = *pSrc;
|
||||
|
||||
pSrc = pSrc->next;
|
||||
pDest = pDest->next;
|
||||
}
|
||||
|
||||
mCurRect = mRectListHead.next;
|
||||
mBoundRect = aRegion.mBoundRect;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Copy (const nsRect& aRect)
|
||||
{
|
||||
Empty ();
|
||||
|
||||
if (!aRect.IsEmpty ())
|
||||
{
|
||||
InsertAfter (new RgnRect (aRect), &mRectListHead);
|
||||
mBoundRect = aRect;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::And (const nsRegion& aRgn1, const nsRegion& aRgn2)
|
||||
{
|
||||
if (&aRgn1 == &aRgn2) // And with self
|
||||
Copy (aRgn1);
|
||||
else
|
||||
if (aRgn1.mRectCount == 0 || aRgn2.mRectCount == 0) // If either region is empty then result is empty
|
||||
Empty ();
|
||||
else
|
||||
{
|
||||
nsRect TmpRect;
|
||||
|
||||
if (aRgn1.mRectCount == 1 && aRgn2.mRectCount == 1) // Intersect rectangle with rectangle
|
||||
{
|
||||
TmpRect.IntersectRect (*aRgn1.mRectListHead.next, *aRgn2.mRectListHead.next);
|
||||
Copy (TmpRect);
|
||||
} else // Intersect region with region
|
||||
{
|
||||
nsRect BoundRect1, BoundRect2;
|
||||
aRgn1.GetBoundRect (&BoundRect1);
|
||||
aRgn2.GetBoundRect (&BoundRect2);
|
||||
|
||||
if (!TmpRect.IntersectRect (BoundRect1, BoundRect2)) // Regions do not intersect
|
||||
Empty ();
|
||||
else
|
||||
{
|
||||
nsRegion TmpRegion;
|
||||
const RgnRect* pSrcRect1 = aRgn1.mRectListHead.next;
|
||||
|
||||
while (pSrcRect1 != &aRgn1.mRectListHead)
|
||||
{
|
||||
const RgnRect* pSrcRect2 = aRgn2.mRectListHead.next;
|
||||
|
||||
while (pSrcRect2 != &aRgn2.mRectListHead)
|
||||
{
|
||||
if (TmpRect.IntersectRect (*pSrcRect1, *pSrcRect2))
|
||||
TmpRegion.InsertInPlace (new RgnRect (TmpRect));
|
||||
|
||||
pSrcRect2 = pSrcRect2->next;
|
||||
}
|
||||
|
||||
pSrcRect1 = pSrcRect1->next;
|
||||
}
|
||||
|
||||
TmpRegion.Optimize ();
|
||||
Copy (TmpRegion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::And (const nsRegion& aRegion, const nsRect& aRect)
|
||||
{
|
||||
// If either region or rectangle is empty then result is empty
|
||||
if (aRegion.mRectCount == 0 || aRect.IsEmpty ())
|
||||
Empty ();
|
||||
else // Intersect region with rectangle
|
||||
{
|
||||
nsRect TmpRect;
|
||||
|
||||
if (aRegion.mRectCount == 1) // Intersect rectangle with rectangle
|
||||
{
|
||||
TmpRect.IntersectRect (*aRegion.mRectListHead.next, aRect);
|
||||
Copy (TmpRect);
|
||||
} else // Intersect complex region with rectangle
|
||||
{
|
||||
aRegion.GetBoundRect (&TmpRect);
|
||||
|
||||
if (!TmpRect.IntersectRect (TmpRect, aRect)) // Rectangle does not intersect region
|
||||
Empty ();
|
||||
else
|
||||
{
|
||||
nsRegion TmpRegion;
|
||||
const RgnRect* pSrcRect = aRegion.mRectListHead.next;
|
||||
|
||||
while (pSrcRect != &aRegion.mRectListHead)
|
||||
{
|
||||
if (TmpRect.IntersectRect (*pSrcRect, aRect))
|
||||
TmpRegion.InsertInPlace (new RgnRect (TmpRect));
|
||||
|
||||
pSrcRect = pSrcRect->next;
|
||||
}
|
||||
|
||||
TmpRegion.Optimize ();
|
||||
Copy (TmpRegion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Or (const nsRegion& aRgn1, const nsRegion& aRgn2)
|
||||
{
|
||||
if (&aRgn1 == &aRgn2) // Or with self
|
||||
Copy (aRgn1);
|
||||
else
|
||||
if (aRgn1.mRectCount == 0) // Region empty. Result is equal to other region
|
||||
Copy (aRgn2);
|
||||
else
|
||||
if (aRgn2.mRectCount == 0) // Region empty. Result is equal to other region
|
||||
Copy (aRgn1);
|
||||
else
|
||||
{
|
||||
nsRect BoundRect1, BoundRect2;
|
||||
aRgn1.GetBoundRect (&BoundRect1);
|
||||
aRgn2.GetBoundRect (&BoundRect2);
|
||||
|
||||
if (!BoundRect1.IntersectRect (BoundRect1, BoundRect2)) // Regions do not intersect
|
||||
Merge (aRgn1, aRgn2);
|
||||
else
|
||||
{
|
||||
nsRegion TmpRegion;
|
||||
TmpRegion.Sub (aRgn1, aRgn2); // Get only parts of region which not overlap the other region
|
||||
Merge (TmpRegion, aRgn2);
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Or (const nsRegion& aRegion, const nsRect& aRect)
|
||||
{
|
||||
if (aRegion.mRectCount == 0) // Region empty. Result is equal to rectangle
|
||||
Copy (aRect);
|
||||
else
|
||||
if (aRect.IsEmpty ()) // Rectangle is empty. Result is equal to region
|
||||
Copy (aRegion);
|
||||
else
|
||||
{
|
||||
nsRect BoundRect, TmpRect;
|
||||
aRegion.GetBoundRect (&BoundRect);
|
||||
|
||||
if (!TmpRect.IntersectRect (BoundRect, aRect)) // Rectangle does not intersect region
|
||||
{
|
||||
Copy (aRegion);
|
||||
InsertInPlace (new RgnRect (aRect), PR_TRUE);
|
||||
} else
|
||||
{
|
||||
if (aRect.Contains (BoundRect)) // Rectangle fully overlays region
|
||||
Copy (aRect);
|
||||
else
|
||||
{
|
||||
SubRectFromRegion (aRegion, aRect); // Exclude from region parts that overlap the rectangle
|
||||
InsertInPlace (new RgnRect (aRect));
|
||||
Optimize ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Xor (const nsRegion& aRgn1, const nsRegion& aRgn2)
|
||||
{
|
||||
if (&aRgn1 == &aRgn2) // Xor with self
|
||||
Empty ();
|
||||
else
|
||||
if (aRgn1.mRectCount == 0) // Region empty. Result is equal to other region
|
||||
Copy (aRgn2);
|
||||
else
|
||||
if (aRgn2.mRectCount == 0) // Region empty. Result is equal to other region
|
||||
Copy (aRgn1);
|
||||
else
|
||||
{
|
||||
nsRect BoundRect1, BoundRect2;
|
||||
aRgn1.GetBoundRect (&BoundRect1);
|
||||
aRgn2.GetBoundRect (&BoundRect2);
|
||||
|
||||
if (!BoundRect1.IntersectRect (BoundRect1, BoundRect2)) // Regions do not intersect
|
||||
Merge (aRgn1, aRgn2);
|
||||
else
|
||||
{
|
||||
nsRegion TmpRegion1, TmpRegion2;
|
||||
TmpRegion2.And (aRgn1, aRgn2); // Overlay
|
||||
TmpRegion1.Sub (aRgn1, TmpRegion2); // aRgn1 - Overlay
|
||||
TmpRegion2.Sub (aRgn2, TmpRegion2); // aRgn2 - Overlay
|
||||
Merge (TmpRegion1, TmpRegion2);
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Xor (const nsRegion& aRegion, const nsRect& aRect)
|
||||
{
|
||||
if (aRegion.mRectCount == 0) // Region empty. Result is equal to rectangle
|
||||
Copy (aRect);
|
||||
else
|
||||
if (aRect.IsEmpty ()) // Rectangle is empty. Result is equal to region
|
||||
Copy (aRegion);
|
||||
else
|
||||
{
|
||||
nsRect BoundRect, TmpRect;
|
||||
aRegion.GetBoundRect (&BoundRect);
|
||||
|
||||
if (!TmpRect.IntersectRect (BoundRect, aRect)) // Rectangle does not intersect region
|
||||
{
|
||||
Copy (aRegion);
|
||||
InsertInPlace (new RgnRect (aRect), PR_TRUE);
|
||||
} else
|
||||
{
|
||||
if (aRect.Contains (BoundRect)) // Rectangle fully overlays region
|
||||
{
|
||||
nsRegion TmpRegion;
|
||||
TmpRegion.Copy (aRect);
|
||||
Sub (TmpRegion, aRegion);
|
||||
} else
|
||||
{
|
||||
nsRegion IntersectRegion, TmpRegion1, TmpRegion2;
|
||||
IntersectRegion.And (aRegion, aRect);
|
||||
TmpRegion1.Sub (aRegion, IntersectRegion);
|
||||
TmpRegion2.Copy (aRect);
|
||||
TmpRegion2.Sub (TmpRegion2, IntersectRegion);
|
||||
Merge (TmpRegion1, TmpRegion2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Sub (const nsRegion& aRgn1, const nsRegion& aRgn2)
|
||||
{
|
||||
if (&aRgn1 == &aRgn2) // Sub from self
|
||||
Empty ();
|
||||
else
|
||||
if (aRgn1.mRectCount == 0) // If source is empty then result is empty, too
|
||||
Empty ();
|
||||
else
|
||||
if (aRgn2.mRectCount == 0) // Nothing to subtract
|
||||
Copy (aRgn1);
|
||||
else
|
||||
{
|
||||
nsRect BoundRect1, BoundRect2;
|
||||
aRgn1.GetBoundRect (&BoundRect1);
|
||||
aRgn2.GetBoundRect (&BoundRect2);
|
||||
|
||||
if (!BoundRect1.IntersectRect (BoundRect1, BoundRect2)) // Regions do not intersect
|
||||
Copy (aRgn1);
|
||||
else
|
||||
{
|
||||
nsRegion TmpRegion;
|
||||
const RgnRect* pRect = aRgn2.mRectListHead.next;
|
||||
|
||||
TmpRegion.SubRectFromRegion (aRgn1, *pRect);
|
||||
pRect = pRect->next;
|
||||
|
||||
while (pRect != &aRgn2.mRectListHead)
|
||||
{
|
||||
TmpRegion.SubRectFromRegion (TmpRegion, *pRect);
|
||||
pRect = pRect->next;
|
||||
}
|
||||
|
||||
TmpRegion.Optimize ();
|
||||
Copy (TmpRegion);
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Sub (const nsRegion& aRegion, const nsRect& aRect)
|
||||
{
|
||||
if (aRegion.mRectCount == 0) // If source is empty then result is empty, too
|
||||
Empty ();
|
||||
else
|
||||
if (aRect.IsEmpty ()) // Nothing to subtract
|
||||
Copy (aRegion);
|
||||
else
|
||||
{
|
||||
nsRect BoundRect, TmpRect;
|
||||
aRegion.GetBoundRect (&BoundRect);
|
||||
|
||||
if (!TmpRect.IntersectRect (BoundRect, aRect)) // Rectangle does not intersect region
|
||||
Copy (aRegion);
|
||||
else
|
||||
{
|
||||
if (aRect.Contains (BoundRect)) // Rectangle fully overlays region
|
||||
Empty ();
|
||||
else
|
||||
{
|
||||
SubRectFromRegion (aRegion, aRect);
|
||||
Optimize ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void nsRegion::SubRectFromRegion (const nsRegion& aRegion, const nsRect& aRect)
|
||||
{
|
||||
nsRegion TmpRegion;
|
||||
TmpRegion.Copy (aRegion);
|
||||
Empty ();
|
||||
|
||||
const RgnRect* pSrcRect = TmpRegion.mRectListHead.next;
|
||||
|
||||
while (pSrcRect != &TmpRegion.mRectListHead)
|
||||
{
|
||||
nsRect TmpRect;
|
||||
|
||||
if (!TmpRect.IntersectRect (*pSrcRect, aRect))
|
||||
InsertInPlace (new RgnRect (*pSrcRect));
|
||||
else
|
||||
{
|
||||
// Rectangle A. Subtract from this rectangle B
|
||||
const nscoord ax = pSrcRect->x;
|
||||
const nscoord axm = pSrcRect->XMost ();
|
||||
const nscoord aw = pSrcRect->width;
|
||||
const nscoord ay = pSrcRect->y;
|
||||
const nscoord aym = pSrcRect->YMost ();
|
||||
const nscoord ah = pSrcRect->height;
|
||||
// Rectangle B. Subtract this from rectangle A
|
||||
const nscoord bx = aRect.x;
|
||||
const nscoord bxm = aRect.XMost ();
|
||||
const nscoord bw = aRect.width;
|
||||
const nscoord by = aRect.y;
|
||||
const nscoord bym = aRect.YMost ();
|
||||
const nscoord bh = aRect.height;
|
||||
// Rectangle I. Area where rectangles A and B intersect
|
||||
const nscoord ix = TmpRect.x;
|
||||
const nscoord ixm = TmpRect.XMost ();
|
||||
const nscoord iw = TmpRect.width;
|
||||
const nscoord iy = TmpRect.y;
|
||||
const nscoord iym = TmpRect.YMost ();
|
||||
const nscoord ih = TmpRect.height;
|
||||
|
||||
// There are 16 combinations how rectangles could intersect
|
||||
|
||||
if (bx <= ax && by <= ay)
|
||||
{
|
||||
if (bxm < axm && bym < aym) // 1.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ixm, ay, axm - ixm, ih));
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm >= axm && bym < aym) // 2.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm < axm && bym >= aym) // 3.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ixm, ay, axm - ixm, ah));
|
||||
}
|
||||
} else
|
||||
if (bx > ax && by <= ay)
|
||||
{
|
||||
if (bxm < axm && bym < aym) // 5.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, ix - ax, ih));
|
||||
InsertInPlace (new RgnRect (ixm, ay, axm - ixm, ih));
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm >= axm && bym < aym) // 6.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, ix - ax, ih));
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm < axm && bym >= aym) // 7.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, ix - ax, ah));
|
||||
InsertInPlace (new RgnRect (ixm, ay, axm - ixm, ah));
|
||||
} else
|
||||
if (bxm >= axm && bym >= aym) // 8.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, ix - ax, ah));
|
||||
}
|
||||
} else
|
||||
if (bx <= ax && by > ay)
|
||||
{
|
||||
if (bxm < axm && bym < aym) // 9.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
InsertInPlace (new RgnRect (ixm, iy, axm - ixm, ih));
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm >= axm && bym < aym) // 10.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm < axm && bym >= aym) // 11.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
InsertInPlace (new RgnRect (ixm, iy, axm - ixm, ih));
|
||||
} else
|
||||
if (bxm >= axm && bym >= aym) // 12.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
}
|
||||
} else
|
||||
if (bx > ax && by > ay)
|
||||
{
|
||||
if (bxm < axm && bym < aym) // 13.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
InsertInPlace (new RgnRect (ax, iy, ix - ax, ih));
|
||||
InsertInPlace (new RgnRect (ixm, iy, axm - ixm, ih));
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm >= axm && bym < aym) // 14.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
InsertInPlace (new RgnRect (ax, iy, ix - ax, ih));
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm < axm && bym >= aym) // 15.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
InsertInPlace (new RgnRect (ax, iy, ix - ax, ih));
|
||||
InsertInPlace (new RgnRect (ixm, iy, axm - ixm, ih));
|
||||
} else
|
||||
if (bxm >= axm && bym >= aym) // 16.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
InsertInPlace (new RgnRect (ax, iy, ix - ax, ih));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pSrcRect = pSrcRect->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PRBool nsRegion::IsEqual (const nsRegion& aRegion) const
|
||||
{
|
||||
if (mRectCount == 0)
|
||||
return (aRegion.mRectCount == 0) ? PR_TRUE : PR_FALSE;
|
||||
|
||||
if (aRegion.mRectCount == 0)
|
||||
return (mRectCount == 0) ? PR_TRUE : PR_FALSE;
|
||||
|
||||
if (mRectCount == 1 && aRegion.mRectCount == 1) // Both regions are simple rectangles
|
||||
{
|
||||
return (*mRectListHead.next == *aRegion.mRectListHead.next);
|
||||
} else // At least one is complex region.
|
||||
{
|
||||
nsRegion TmpRegion;
|
||||
TmpRegion.Xor (*this, aRegion); // Get difference between two regions
|
||||
|
||||
return (TmpRegion.mRectCount == 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void nsRegion::Offset (PRInt32 aXOffset, PRInt32 aYOffset)
|
||||
{
|
||||
if (aXOffset || aYOffset)
|
||||
{
|
||||
RgnRect* pRect = mRectListHead.next;
|
||||
|
||||
while (pRect != &mRectListHead)
|
||||
{
|
||||
pRect->MoveBy (aXOffset, aYOffset);
|
||||
pRect = pRect->next;
|
||||
}
|
||||
|
||||
mBoundRect.MoveBy (aXOffset, aYOffset);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,865 @@
|
|||
/*
|
||||
* The contents of this file are subject to the Mozilla Public
|
||||
* License Version 1.1 (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.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Dainis Jonitis,
|
||||
* <Dainis_Jonitis@swh-t.lv>. Portions created by Dainis Jonitis are
|
||||
* Copyright (C) 2001 Dainis Jonitis. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*/
|
||||
|
||||
#include "prlock.h"
|
||||
#include "nsRegion.h"
|
||||
|
||||
|
||||
// Custom memory allocator for nsRegion::RgnRect structures.
|
||||
// Entries are allocated from global memory pool.
|
||||
// Memory pool can grow in size, but it can't shrink.
|
||||
|
||||
#define MEM_CHUNK_SIZE 100
|
||||
|
||||
class RgnRectMemoryAllocator
|
||||
{
|
||||
nsRegion::RgnRect* mFreeListHead;
|
||||
PRUint32 mFreeEntries;
|
||||
void* mChunkListHead;
|
||||
PRLock* mLock;
|
||||
|
||||
|
||||
void Lock () { PR_Lock (mLock); }
|
||||
void Unlock () { PR_Unlock (mLock); }
|
||||
|
||||
void* AllocChunk (PRUint32 aEntries, void* aNextChunk, nsRegion::RgnRect* aTailDest)
|
||||
{
|
||||
PRUint8* pBuf = new PRUint8 [aEntries * sizeof (nsRegion::RgnRect) + sizeof (void*)];
|
||||
*NS_STATIC_CAST (void**, pBuf) = aNextChunk;
|
||||
nsRegion::RgnRect* pRect = NS_STATIC_CAST (nsRegion::RgnRect*, pBuf + sizeof (void*));
|
||||
|
||||
for (PRUint32 cnt = 0 ; cnt < aEntries - 1 ; cnt++)
|
||||
pRect [cnt].next = &pRect [cnt + 1];
|
||||
|
||||
pRect [aEntries - 1].next = aTailDest;
|
||||
|
||||
return pBuf;
|
||||
}
|
||||
|
||||
void FreeChunk (void* aChunk) { delete [] aChunk; }
|
||||
void* NextChunk (const void* aThisChunk) const { return *NS_STATIC_CAST (void**, aThisChunk); }
|
||||
|
||||
nsRegion::RgnRect* ChunkHead (const void* aThisChunk) const
|
||||
{ return NS_STATIC_CAST (nsRegion::RgnRect*, NS_STATIC_CAST (PRUint8*, aThisChunk) + sizeof (void*)); }
|
||||
|
||||
public:
|
||||
RgnRectMemoryAllocator (PRUint32 aNumOfEntries);
|
||||
~RgnRectMemoryAllocator ();
|
||||
|
||||
inline nsRegion::RgnRect* Alloc ();
|
||||
inline void Free (nsRegion::RgnRect* aRect);
|
||||
};
|
||||
|
||||
|
||||
RgnRectMemoryAllocator::RgnRectMemoryAllocator (PRUint32 aNumOfEntries)
|
||||
{
|
||||
mLock = PR_NewLock ();
|
||||
mChunkListHead = AllocChunk (aNumOfEntries, nsnull, nsnull);
|
||||
mFreeEntries = aNumOfEntries;
|
||||
mFreeListHead = ChunkHead (mChunkListHead);
|
||||
}
|
||||
|
||||
RgnRectMemoryAllocator::~RgnRectMemoryAllocator ()
|
||||
{
|
||||
while (mChunkListHead)
|
||||
{
|
||||
void* tmp = mChunkListHead;
|
||||
mChunkListHead = NextChunk (mChunkListHead);
|
||||
FreeChunk (tmp);
|
||||
}
|
||||
|
||||
PR_DestroyLock (mLock);
|
||||
}
|
||||
|
||||
inline nsRegion::RgnRect* RgnRectMemoryAllocator::Alloc ()
|
||||
{
|
||||
Lock ();
|
||||
|
||||
if (mFreeEntries == 0)
|
||||
{
|
||||
mChunkListHead = AllocChunk (MEM_CHUNK_SIZE, mChunkListHead, mFreeListHead);
|
||||
mFreeEntries = MEM_CHUNK_SIZE;
|
||||
mFreeListHead = ChunkHead (mChunkListHead);
|
||||
}
|
||||
|
||||
nsRegion::RgnRect* tmp = mFreeListHead;
|
||||
mFreeListHead = mFreeListHead->next;
|
||||
mFreeEntries--;
|
||||
Unlock ();
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
inline void RgnRectMemoryAllocator::Free (nsRegion::RgnRect* aRect)
|
||||
{
|
||||
Lock ();
|
||||
mFreeEntries++;
|
||||
aRect->next = mFreeListHead;
|
||||
mFreeListHead = aRect;
|
||||
Unlock ();
|
||||
}
|
||||
|
||||
|
||||
// Global pool for nsRegion::RgnRect allocation
|
||||
static RgnRectMemoryAllocator gRectPool (MEM_CHUNK_SIZE);
|
||||
|
||||
|
||||
void* nsRegion::RgnRect::operator new (size_t)
|
||||
{
|
||||
return gRectPool.Alloc ();
|
||||
}
|
||||
|
||||
void nsRegion::RgnRect::operator delete (void* aRect, size_t)
|
||||
{
|
||||
gRectPool.Free (NS_STATIC_CAST (RgnRect*, aRect));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
nsRegion::nsRegion ()
|
||||
{
|
||||
mRectListHead.prev = mRectListHead.next = &mRectListHead;
|
||||
mRectListHead.width = mRectListHead.height = -100; // This is dummy marker node
|
||||
mCurRect = &mRectListHead;
|
||||
mRectCount = 0;
|
||||
mBoundRect.SetRect (0, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
void nsRegion::Empty ()
|
||||
{
|
||||
mCurRect = mRectListHead.next;
|
||||
|
||||
while (mCurRect != &mRectListHead)
|
||||
{
|
||||
RgnRect* tmp = mCurRect;
|
||||
mCurRect = mCurRect->next;
|
||||
delete tmp;
|
||||
}
|
||||
|
||||
mRectCount = 0;
|
||||
mRectListHead.prev = mRectListHead.next = &mRectListHead;
|
||||
mBoundRect.SetRect (0, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
// Insert node in right place of sorted list
|
||||
// If necessary then bounding rectangle could be updated and rectangle combined
|
||||
// with neighbour rectangles. This is usually done in Optimize ()
|
||||
|
||||
void nsRegion::InsertInPlace (RgnRect* aRect, PRBool aOptimizeOnFly)
|
||||
{
|
||||
if (mRectCount == 0)
|
||||
InsertAfter (aRect, &mRectListHead);
|
||||
else
|
||||
{
|
||||
if (aRect->y > mCurRect->y)
|
||||
{
|
||||
mRectListHead.y = 2147483647;
|
||||
|
||||
while (aRect->y > mCurRect->next->y)
|
||||
mCurRect = mCurRect->next;
|
||||
|
||||
while (aRect->y == mCurRect->next->y && aRect->x > mCurRect->next->x)
|
||||
mCurRect = mCurRect->next;
|
||||
|
||||
InsertAfter (aRect, mCurRect);
|
||||
} else
|
||||
if (aRect->y < mCurRect->y)
|
||||
{
|
||||
mRectListHead.y = -2147483647;
|
||||
|
||||
while (aRect->y < mCurRect->prev->y)
|
||||
mCurRect = mCurRect->prev;
|
||||
|
||||
while (aRect->y == mCurRect->prev->y && aRect->x < mCurRect->prev->x)
|
||||
mCurRect = mCurRect->prev;
|
||||
|
||||
InsertBefore (aRect, mCurRect);
|
||||
} else
|
||||
{
|
||||
if (aRect->x > mCurRect->x)
|
||||
{
|
||||
mRectListHead.y = 2147483647;
|
||||
|
||||
while (aRect->y == mCurRect->next->y && aRect->x > mCurRect->next->x)
|
||||
mCurRect = mCurRect->next;
|
||||
|
||||
InsertAfter (aRect, mCurRect);
|
||||
} else
|
||||
{
|
||||
mRectListHead.y = -2147483647;
|
||||
|
||||
while (aRect->y == mCurRect->prev->y && aRect->x < mCurRect->prev->x)
|
||||
mCurRect = mCurRect->prev;
|
||||
|
||||
InsertBefore (aRect, mCurRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (aOptimizeOnFly)
|
||||
{
|
||||
if (mRectCount == 1)
|
||||
mBoundRect = *mCurRect;
|
||||
else
|
||||
{
|
||||
mBoundRect.UnionRect (mBoundRect, *mCurRect);
|
||||
|
||||
// Check if we can go left or up before starting to combine rectangles
|
||||
if ((mCurRect->y == mCurRect->prev->y && mCurRect->height == mCurRect->prev->height &&
|
||||
mCurRect->x == mCurRect->prev->XMost ()) ||
|
||||
(mCurRect->x == mCurRect->prev->x && mCurRect->width == mCurRect->prev->width &&
|
||||
mCurRect->y == mCurRect->prev->YMost ()) )
|
||||
mCurRect = mCurRect->prev;
|
||||
|
||||
// Try to combine with rectangle on right side
|
||||
while (mCurRect->y == mCurRect->next->y && mCurRect->height == mCurRect->next->height &&
|
||||
mCurRect->XMost () == mCurRect->next->x)
|
||||
{
|
||||
mCurRect->width += mCurRect->next->width;
|
||||
delete Remove (mCurRect->next);
|
||||
}
|
||||
|
||||
// Try to combine with rectangle under this one
|
||||
while (mCurRect->x == mCurRect->next->x && mCurRect->width == mCurRect->next->width &&
|
||||
mCurRect->YMost () == mCurRect->next->y)
|
||||
{
|
||||
mCurRect->height += mCurRect->next->height;
|
||||
delete Remove (mCurRect->next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
nsRegion::RgnRect* nsRegion::Remove (RgnRect* aRect)
|
||||
{
|
||||
aRect->prev->next = aRect->next;
|
||||
aRect->next->prev = aRect->prev;
|
||||
mRectCount--;
|
||||
|
||||
if (mCurRect == aRect)
|
||||
mCurRect = (aRect->next != &mRectListHead) ? aRect->next : aRect->prev;
|
||||
|
||||
return aRect;
|
||||
}
|
||||
|
||||
|
||||
// Try to reduce the number of rectangles in complex region by combining with
|
||||
// surrounding ones on right and bottom sides of each rectangle in list.
|
||||
// Update bounding rectangle
|
||||
|
||||
void nsRegion::Optimize ()
|
||||
{
|
||||
mBoundRect.SetRect (0, 0, 0, 0);
|
||||
RgnRect* pRect = mRectListHead.next;
|
||||
|
||||
while (pRect != &mRectListHead)
|
||||
{
|
||||
// Try to combine with rectangle on right side
|
||||
while (pRect->y == pRect->next->y && pRect->height == pRect->next->height &&
|
||||
pRect->XMost () == pRect->next->x)
|
||||
{
|
||||
pRect->width += pRect->next->width;
|
||||
delete Remove (pRect->next);
|
||||
}
|
||||
|
||||
// Try to combine with rectangle under this one
|
||||
while (pRect->x == pRect->next->x && pRect->width == pRect->next->width &&
|
||||
pRect->YMost () == pRect->next->y)
|
||||
{
|
||||
pRect->height += pRect->next->height;
|
||||
delete Remove (pRect->next);
|
||||
}
|
||||
|
||||
mBoundRect.UnionRect (mBoundRect, *pRect);
|
||||
pRect = pRect->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Merge two non-overlapping regions into one.
|
||||
|
||||
void nsRegion::Merge (const nsRegion& aRgn1, const nsRegion& aRgn2)
|
||||
{
|
||||
if (aRgn1.mRectCount == 0) // Region empty. Result is equal to other region
|
||||
Copy (aRgn2);
|
||||
else
|
||||
if (aRgn2.mRectCount == 0) // Region empty. Result is equal to other region
|
||||
Copy (aRgn1);
|
||||
else
|
||||
{
|
||||
const nsRegion* pCopyRegion, *pInsertRegion;
|
||||
|
||||
// Determine which region contains more rectangles. Copy the larger one
|
||||
if (aRgn1.mRectCount >= aRgn2.mRectCount)
|
||||
{
|
||||
pCopyRegion = &aRgn1;
|
||||
pInsertRegion = &aRgn2;
|
||||
} else
|
||||
{
|
||||
pCopyRegion = &aRgn2;
|
||||
pInsertRegion = &aRgn1;
|
||||
}
|
||||
|
||||
if (pInsertRegion == this) // Do merge in-place
|
||||
pInsertRegion = pCopyRegion;
|
||||
else
|
||||
Copy (*pCopyRegion);
|
||||
|
||||
const RgnRect* pSrcRect = pInsertRegion->mRectListHead.next;
|
||||
|
||||
while (pSrcRect != &pInsertRegion->mRectListHead)
|
||||
{
|
||||
InsertInPlace (new RgnRect (*pSrcRect));
|
||||
|
||||
pSrcRect = pSrcRect->next;
|
||||
}
|
||||
|
||||
Optimize ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Copy (const nsRegion& aRegion)
|
||||
{
|
||||
if (&aRegion == this)
|
||||
return *this;
|
||||
|
||||
if (aRegion.mRectCount == 0)
|
||||
Empty ();
|
||||
else
|
||||
{
|
||||
while (mRectCount < aRegion.mRectCount) // Less rectangles than in source. Add missing ones
|
||||
InsertAfter (new RgnRect, &mRectListHead);
|
||||
|
||||
while (mRectCount > aRegion.mRectCount) // More rectangles than in source. Remove unnecessary
|
||||
delete Remove (mRectListHead.next);
|
||||
|
||||
const RgnRect* pSrc = aRegion.mRectListHead.next;
|
||||
RgnRect* pDest = mRectListHead.next;
|
||||
|
||||
while (pSrc != &aRegion.mRectListHead)
|
||||
{
|
||||
*pDest = *pSrc;
|
||||
|
||||
pSrc = pSrc->next;
|
||||
pDest = pDest->next;
|
||||
}
|
||||
|
||||
mCurRect = mRectListHead.next;
|
||||
mBoundRect = aRegion.mBoundRect;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Copy (const nsRect& aRect)
|
||||
{
|
||||
Empty ();
|
||||
|
||||
if (!aRect.IsEmpty ())
|
||||
{
|
||||
InsertAfter (new RgnRect (aRect), &mRectListHead);
|
||||
mBoundRect = aRect;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::And (const nsRegion& aRgn1, const nsRegion& aRgn2)
|
||||
{
|
||||
if (&aRgn1 == &aRgn2) // And with self
|
||||
Copy (aRgn1);
|
||||
else
|
||||
if (aRgn1.mRectCount == 0 || aRgn2.mRectCount == 0) // If either region is empty then result is empty
|
||||
Empty ();
|
||||
else
|
||||
{
|
||||
nsRect TmpRect;
|
||||
|
||||
if (aRgn1.mRectCount == 1 && aRgn2.mRectCount == 1) // Intersect rectangle with rectangle
|
||||
{
|
||||
TmpRect.IntersectRect (*aRgn1.mRectListHead.next, *aRgn2.mRectListHead.next);
|
||||
Copy (TmpRect);
|
||||
} else // Intersect region with region
|
||||
{
|
||||
nsRect BoundRect1, BoundRect2;
|
||||
aRgn1.GetBoundRect (&BoundRect1);
|
||||
aRgn2.GetBoundRect (&BoundRect2);
|
||||
|
||||
if (!TmpRect.IntersectRect (BoundRect1, BoundRect2)) // Regions do not intersect
|
||||
Empty ();
|
||||
else
|
||||
{
|
||||
nsRegion TmpRegion;
|
||||
const RgnRect* pSrcRect1 = aRgn1.mRectListHead.next;
|
||||
|
||||
while (pSrcRect1 != &aRgn1.mRectListHead)
|
||||
{
|
||||
const RgnRect* pSrcRect2 = aRgn2.mRectListHead.next;
|
||||
|
||||
while (pSrcRect2 != &aRgn2.mRectListHead)
|
||||
{
|
||||
if (TmpRect.IntersectRect (*pSrcRect1, *pSrcRect2))
|
||||
TmpRegion.InsertInPlace (new RgnRect (TmpRect));
|
||||
|
||||
pSrcRect2 = pSrcRect2->next;
|
||||
}
|
||||
|
||||
pSrcRect1 = pSrcRect1->next;
|
||||
}
|
||||
|
||||
TmpRegion.Optimize ();
|
||||
Copy (TmpRegion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::And (const nsRegion& aRegion, const nsRect& aRect)
|
||||
{
|
||||
// If either region or rectangle is empty then result is empty
|
||||
if (aRegion.mRectCount == 0 || aRect.IsEmpty ())
|
||||
Empty ();
|
||||
else // Intersect region with rectangle
|
||||
{
|
||||
nsRect TmpRect;
|
||||
|
||||
if (aRegion.mRectCount == 1) // Intersect rectangle with rectangle
|
||||
{
|
||||
TmpRect.IntersectRect (*aRegion.mRectListHead.next, aRect);
|
||||
Copy (TmpRect);
|
||||
} else // Intersect complex region with rectangle
|
||||
{
|
||||
aRegion.GetBoundRect (&TmpRect);
|
||||
|
||||
if (!TmpRect.IntersectRect (TmpRect, aRect)) // Rectangle does not intersect region
|
||||
Empty ();
|
||||
else
|
||||
{
|
||||
nsRegion TmpRegion;
|
||||
const RgnRect* pSrcRect = aRegion.mRectListHead.next;
|
||||
|
||||
while (pSrcRect != &aRegion.mRectListHead)
|
||||
{
|
||||
if (TmpRect.IntersectRect (*pSrcRect, aRect))
|
||||
TmpRegion.InsertInPlace (new RgnRect (TmpRect));
|
||||
|
||||
pSrcRect = pSrcRect->next;
|
||||
}
|
||||
|
||||
TmpRegion.Optimize ();
|
||||
Copy (TmpRegion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Or (const nsRegion& aRgn1, const nsRegion& aRgn2)
|
||||
{
|
||||
if (&aRgn1 == &aRgn2) // Or with self
|
||||
Copy (aRgn1);
|
||||
else
|
||||
if (aRgn1.mRectCount == 0) // Region empty. Result is equal to other region
|
||||
Copy (aRgn2);
|
||||
else
|
||||
if (aRgn2.mRectCount == 0) // Region empty. Result is equal to other region
|
||||
Copy (aRgn1);
|
||||
else
|
||||
{
|
||||
nsRect BoundRect1, BoundRect2;
|
||||
aRgn1.GetBoundRect (&BoundRect1);
|
||||
aRgn2.GetBoundRect (&BoundRect2);
|
||||
|
||||
if (!BoundRect1.IntersectRect (BoundRect1, BoundRect2)) // Regions do not intersect
|
||||
{
|
||||
Merge (aRgn1, aRgn2);
|
||||
Optimize ();
|
||||
} else
|
||||
{
|
||||
nsRegion TmpRegion;
|
||||
TmpRegion.Sub (aRgn1, aRgn2); // Get only parts of region which not overlap the other region
|
||||
Merge (TmpRegion, aRgn2);
|
||||
Optimize ();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Or (const nsRegion& aRegion, const nsRect& aRect)
|
||||
{
|
||||
if (aRegion.mRectCount == 0) // Region empty. Result is equal to rectangle
|
||||
Copy (aRect);
|
||||
else
|
||||
if (aRect.IsEmpty ()) // Rectangle is empty. Result is equal to region
|
||||
Copy (aRegion);
|
||||
else
|
||||
{
|
||||
nsRect BoundRect, TmpRect;
|
||||
aRegion.GetBoundRect (&BoundRect);
|
||||
|
||||
if (!TmpRect.IntersectRect (BoundRect, aRect)) // Rectangle does not intersect region
|
||||
{
|
||||
Copy (aRegion);
|
||||
InsertInPlace (new RgnRect (aRect), PR_TRUE);
|
||||
} else
|
||||
{
|
||||
if (aRect.Contains (BoundRect)) // Rectangle fully overlays region
|
||||
Copy (aRect);
|
||||
else
|
||||
{
|
||||
SubRectFromRegion (aRegion, aRect); // Exclude from region parts that overlap the rectangle
|
||||
InsertInPlace (new RgnRect (aRect));
|
||||
Optimize ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Xor (const nsRegion& aRgn1, const nsRegion& aRgn2)
|
||||
{
|
||||
if (&aRgn1 == &aRgn2) // Xor with self
|
||||
Empty ();
|
||||
else
|
||||
if (aRgn1.mRectCount == 0) // Region empty. Result is equal to other region
|
||||
Copy (aRgn2);
|
||||
else
|
||||
if (aRgn2.mRectCount == 0) // Region empty. Result is equal to other region
|
||||
Copy (aRgn1);
|
||||
else
|
||||
{
|
||||
nsRect BoundRect1, BoundRect2;
|
||||
aRgn1.GetBoundRect (&BoundRect1);
|
||||
aRgn2.GetBoundRect (&BoundRect2);
|
||||
|
||||
if (!BoundRect1.IntersectRect (BoundRect1, BoundRect2)) // Regions do not intersect
|
||||
{
|
||||
Merge (aRgn1, aRgn2);
|
||||
Optimize ();
|
||||
} else
|
||||
{
|
||||
nsRegion TmpRegion1, TmpRegion2;
|
||||
TmpRegion2.And (aRgn1, aRgn2); // Overlay
|
||||
TmpRegion1.Sub (aRgn1, TmpRegion2); // aRgn1 - Overlay
|
||||
TmpRegion2.Sub (aRgn2, TmpRegion2); // aRgn2 - Overlay
|
||||
Merge (TmpRegion1, TmpRegion2);
|
||||
Optimize ();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Xor (const nsRegion& aRegion, const nsRect& aRect)
|
||||
{
|
||||
if (aRegion.mRectCount == 0) // Region empty. Result is equal to rectangle
|
||||
Copy (aRect);
|
||||
else
|
||||
if (aRect.IsEmpty ()) // Rectangle is empty. Result is equal to region
|
||||
Copy (aRegion);
|
||||
else
|
||||
{
|
||||
nsRect BoundRect;
|
||||
aRegion.GetBoundRect (&BoundRect);
|
||||
|
||||
if (!BoundRect.IntersectRect (BoundRect, aRect)) // Rectangle does not intersect region
|
||||
{
|
||||
Copy (aRegion);
|
||||
InsertInPlace (new RgnRect (aRect), PR_TRUE);
|
||||
} else
|
||||
{
|
||||
nsRegion IntersectRegion, TmpRegion1, TmpRegion2;
|
||||
IntersectRegion.And (aRegion, aRect);
|
||||
TmpRegion1.Sub (aRegion, IntersectRegion);
|
||||
TmpRegion2.Copy (aRect);
|
||||
TmpRegion2.Sub (TmpRegion2, IntersectRegion);
|
||||
Merge (TmpRegion1, TmpRegion2);
|
||||
Optimize ();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Sub (const nsRegion& aRgn1, const nsRegion& aRgn2)
|
||||
{
|
||||
if (&aRgn1 == &aRgn2) // Sub from self
|
||||
Empty ();
|
||||
else
|
||||
if (aRgn1.mRectCount == 0) // If source is empty then result is empty, too
|
||||
Empty ();
|
||||
else
|
||||
if (aRgn2.mRectCount == 0) // Nothing to subtract
|
||||
Copy (aRgn1);
|
||||
else
|
||||
{
|
||||
nsRect BoundRect1, BoundRect2;
|
||||
aRgn1.GetBoundRect (&BoundRect1);
|
||||
aRgn2.GetBoundRect (&BoundRect2);
|
||||
|
||||
if (!BoundRect1.IntersectRect (BoundRect1, BoundRect2)) // Regions do not intersect
|
||||
Copy (aRgn1);
|
||||
else
|
||||
{
|
||||
nsRegion TmpRegion;
|
||||
const RgnRect* pRect = aRgn2.mRectListHead.next;
|
||||
|
||||
TmpRegion.SubRectFromRegion (aRgn1, *pRect);
|
||||
pRect = pRect->next;
|
||||
|
||||
while (pRect != &aRgn2.mRectListHead)
|
||||
{
|
||||
TmpRegion.SubRectFromRegion (TmpRegion, *pRect);
|
||||
pRect = pRect->next;
|
||||
}
|
||||
|
||||
TmpRegion.Optimize ();
|
||||
Copy (TmpRegion);
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Sub (const nsRegion& aRegion, const nsRect& aRect)
|
||||
{
|
||||
if (aRegion.mRectCount == 0) // If source is empty then result is empty, too
|
||||
Empty ();
|
||||
else
|
||||
if (aRect.IsEmpty ()) // Nothing to subtract
|
||||
Copy (aRegion);
|
||||
else
|
||||
{
|
||||
nsRect BoundRect, TmpRect;
|
||||
aRegion.GetBoundRect (&BoundRect);
|
||||
|
||||
if (!TmpRect.IntersectRect (BoundRect, aRect)) // Rectangle does not intersect region
|
||||
Copy (aRegion);
|
||||
else
|
||||
{
|
||||
if (aRect.Contains (BoundRect)) // Rectangle fully overlays region
|
||||
Empty ();
|
||||
else
|
||||
{
|
||||
SubRectFromRegion (aRegion, aRect);
|
||||
Optimize ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void nsRegion::SubRectFromRegion (const nsRegion& aRegion, const nsRect& aRect)
|
||||
{
|
||||
nsRegion TmpRegion;
|
||||
TmpRegion.Copy (aRegion);
|
||||
Empty ();
|
||||
|
||||
const RgnRect* pSrcRect = TmpRegion.mRectListHead.next;
|
||||
|
||||
while (pSrcRect != &TmpRegion.mRectListHead)
|
||||
{
|
||||
nsRect TmpRect;
|
||||
|
||||
if (!TmpRect.IntersectRect (*pSrcRect, aRect))
|
||||
InsertInPlace (new RgnRect (*pSrcRect));
|
||||
else
|
||||
{
|
||||
// Rectangle A. Subtract from this rectangle B
|
||||
const nscoord ax = pSrcRect->x;
|
||||
const nscoord axm = pSrcRect->XMost ();
|
||||
const nscoord aw = pSrcRect->width;
|
||||
const nscoord ay = pSrcRect->y;
|
||||
const nscoord aym = pSrcRect->YMost ();
|
||||
const nscoord ah = pSrcRect->height;
|
||||
// Rectangle B. Subtract this from rectangle A
|
||||
const nscoord bx = aRect.x;
|
||||
const nscoord bxm = aRect.XMost ();
|
||||
const nscoord bw = aRect.width;
|
||||
const nscoord by = aRect.y;
|
||||
const nscoord bym = aRect.YMost ();
|
||||
const nscoord bh = aRect.height;
|
||||
// Rectangle I. Area where rectangles A and B intersect
|
||||
const nscoord ix = TmpRect.x;
|
||||
const nscoord ixm = TmpRect.XMost ();
|
||||
const nscoord iw = TmpRect.width;
|
||||
const nscoord iy = TmpRect.y;
|
||||
const nscoord iym = TmpRect.YMost ();
|
||||
const nscoord ih = TmpRect.height;
|
||||
|
||||
// There are 16 combinations how rectangles could intersect
|
||||
|
||||
if (bx <= ax && by <= ay)
|
||||
{
|
||||
if (bxm < axm && bym < aym) // 1.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ixm, ay, axm - ixm, ih));
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm >= axm && bym < aym) // 2.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm < axm && bym >= aym) // 3.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ixm, ay, axm - ixm, ah));
|
||||
}
|
||||
} else
|
||||
if (bx > ax && by <= ay)
|
||||
{
|
||||
if (bxm < axm && bym < aym) // 5.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, ix - ax, ih));
|
||||
InsertInPlace (new RgnRect (ixm, ay, axm - ixm, ih));
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm >= axm && bym < aym) // 6.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, ix - ax, ih));
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm < axm && bym >= aym) // 7.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, ix - ax, ah));
|
||||
InsertInPlace (new RgnRect (ixm, ay, axm - ixm, ah));
|
||||
} else
|
||||
if (bxm >= axm && bym >= aym) // 8.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, ix - ax, ah));
|
||||
}
|
||||
} else
|
||||
if (bx <= ax && by > ay)
|
||||
{
|
||||
if (bxm < axm && bym < aym) // 9.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
InsertInPlace (new RgnRect (ixm, iy, axm - ixm, ih));
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm >= axm && bym < aym) // 10.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm < axm && bym >= aym) // 11.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
InsertInPlace (new RgnRect (ixm, iy, axm - ixm, ih));
|
||||
} else
|
||||
if (bxm >= axm && bym >= aym) // 12.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
}
|
||||
} else
|
||||
if (bx > ax && by > ay)
|
||||
{
|
||||
if (bxm < axm && bym < aym) // 13.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
InsertInPlace (new RgnRect (ax, iy, ix - ax, ih));
|
||||
InsertInPlace (new RgnRect (ixm, iy, axm - ixm, ih));
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm >= axm && bym < aym) // 14.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
InsertInPlace (new RgnRect (ax, iy, ix - ax, ih));
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm < axm && bym >= aym) // 15.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
InsertInPlace (new RgnRect (ax, iy, ix - ax, ih));
|
||||
InsertInPlace (new RgnRect (ixm, iy, axm - ixm, ih));
|
||||
} else
|
||||
if (bxm >= axm && bym >= aym) // 16.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
InsertInPlace (new RgnRect (ax, iy, ix - ax, ih));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pSrcRect = pSrcRect->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PRBool nsRegion::IsEqual (const nsRegion& aRegion) const
|
||||
{
|
||||
if (mRectCount == 0)
|
||||
return (aRegion.mRectCount == 0) ? PR_TRUE : PR_FALSE;
|
||||
|
||||
if (aRegion.mRectCount == 0)
|
||||
return (mRectCount == 0) ? PR_TRUE : PR_FALSE;
|
||||
|
||||
if (mRectCount == 1 && aRegion.mRectCount == 1) // Both regions are simple rectangles
|
||||
{
|
||||
return (*mRectListHead.next == *aRegion.mRectListHead.next);
|
||||
} else // At least one is complex region.
|
||||
{
|
||||
nsRegion TmpRegion;
|
||||
TmpRegion.Xor (*this, aRegion); // Get difference between two regions
|
||||
|
||||
return (TmpRegion.mRectCount == 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void nsRegion::Offset (PRInt32 aXOffset, PRInt32 aYOffset)
|
||||
{
|
||||
if (aXOffset || aYOffset)
|
||||
{
|
||||
RgnRect* pRect = mRectListHead.next;
|
||||
|
||||
while (pRect != &mRectListHead)
|
||||
{
|
||||
pRect->x += aXOffset;
|
||||
pRect->y += aYOffset;
|
||||
|
||||
pRect = pRect->next;
|
||||
}
|
||||
|
||||
mBoundRect.x += aXOffset;
|
||||
mBoundRect.y += aYOffset;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,865 @@
|
|||
/*
|
||||
* The contents of this file are subject to the Mozilla Public
|
||||
* License Version 1.1 (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.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Dainis Jonitis,
|
||||
* <Dainis_Jonitis@swh-t.lv>. Portions created by Dainis Jonitis are
|
||||
* Copyright (C) 2001 Dainis Jonitis. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*/
|
||||
|
||||
#include "prlock.h"
|
||||
#include "nsRegion.h"
|
||||
|
||||
|
||||
// Custom memory allocator for nsRegion::RgnRect structures.
|
||||
// Entries are allocated from global memory pool.
|
||||
// Memory pool can grow in size, but it can't shrink.
|
||||
|
||||
#define MEM_CHUNK_SIZE 100
|
||||
|
||||
class RgnRectMemoryAllocator
|
||||
{
|
||||
nsRegion::RgnRect* mFreeListHead;
|
||||
PRUint32 mFreeEntries;
|
||||
void* mChunkListHead;
|
||||
PRLock* mLock;
|
||||
|
||||
|
||||
void Lock () { PR_Lock (mLock); }
|
||||
void Unlock () { PR_Unlock (mLock); }
|
||||
|
||||
void* AllocChunk (PRUint32 aEntries, void* aNextChunk, nsRegion::RgnRect* aTailDest)
|
||||
{
|
||||
PRUint8* pBuf = new PRUint8 [aEntries * sizeof (nsRegion::RgnRect) + sizeof (void*)];
|
||||
*NS_STATIC_CAST (void**, pBuf) = aNextChunk;
|
||||
nsRegion::RgnRect* pRect = NS_STATIC_CAST (nsRegion::RgnRect*, pBuf + sizeof (void*));
|
||||
|
||||
for (PRUint32 cnt = 0 ; cnt < aEntries - 1 ; cnt++)
|
||||
pRect [cnt].next = &pRect [cnt + 1];
|
||||
|
||||
pRect [aEntries - 1].next = aTailDest;
|
||||
|
||||
return pBuf;
|
||||
}
|
||||
|
||||
void FreeChunk (void* aChunk) { delete [] aChunk; }
|
||||
void* NextChunk (const void* aThisChunk) const { return *NS_STATIC_CAST (void**, aThisChunk); }
|
||||
|
||||
nsRegion::RgnRect* ChunkHead (const void* aThisChunk) const
|
||||
{ return NS_STATIC_CAST (nsRegion::RgnRect*, NS_STATIC_CAST (PRUint8*, aThisChunk) + sizeof (void*)); }
|
||||
|
||||
public:
|
||||
RgnRectMemoryAllocator (PRUint32 aNumOfEntries);
|
||||
~RgnRectMemoryAllocator ();
|
||||
|
||||
inline nsRegion::RgnRect* Alloc ();
|
||||
inline void Free (nsRegion::RgnRect* aRect);
|
||||
};
|
||||
|
||||
|
||||
RgnRectMemoryAllocator::RgnRectMemoryAllocator (PRUint32 aNumOfEntries)
|
||||
{
|
||||
mLock = PR_NewLock ();
|
||||
mChunkListHead = AllocChunk (aNumOfEntries, nsnull, nsnull);
|
||||
mFreeEntries = aNumOfEntries;
|
||||
mFreeListHead = ChunkHead (mChunkListHead);
|
||||
}
|
||||
|
||||
RgnRectMemoryAllocator::~RgnRectMemoryAllocator ()
|
||||
{
|
||||
while (mChunkListHead)
|
||||
{
|
||||
void* tmp = mChunkListHead;
|
||||
mChunkListHead = NextChunk (mChunkListHead);
|
||||
FreeChunk (tmp);
|
||||
}
|
||||
|
||||
PR_DestroyLock (mLock);
|
||||
}
|
||||
|
||||
inline nsRegion::RgnRect* RgnRectMemoryAllocator::Alloc ()
|
||||
{
|
||||
Lock ();
|
||||
|
||||
if (mFreeEntries == 0)
|
||||
{
|
||||
mChunkListHead = AllocChunk (MEM_CHUNK_SIZE, mChunkListHead, mFreeListHead);
|
||||
mFreeEntries = MEM_CHUNK_SIZE;
|
||||
mFreeListHead = ChunkHead (mChunkListHead);
|
||||
}
|
||||
|
||||
nsRegion::RgnRect* tmp = mFreeListHead;
|
||||
mFreeListHead = mFreeListHead->next;
|
||||
mFreeEntries--;
|
||||
Unlock ();
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
inline void RgnRectMemoryAllocator::Free (nsRegion::RgnRect* aRect)
|
||||
{
|
||||
Lock ();
|
||||
mFreeEntries++;
|
||||
aRect->next = mFreeListHead;
|
||||
mFreeListHead = aRect;
|
||||
Unlock ();
|
||||
}
|
||||
|
||||
|
||||
// Global pool for nsRegion::RgnRect allocation
|
||||
static RgnRectMemoryAllocator gRectPool (MEM_CHUNK_SIZE);
|
||||
|
||||
|
||||
void* nsRegion::RgnRect::operator new (size_t)
|
||||
{
|
||||
return gRectPool.Alloc ();
|
||||
}
|
||||
|
||||
void nsRegion::RgnRect::operator delete (void* aRect, size_t)
|
||||
{
|
||||
gRectPool.Free (NS_STATIC_CAST (RgnRect*, aRect));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
nsRegion::nsRegion ()
|
||||
{
|
||||
mRectListHead.prev = mRectListHead.next = &mRectListHead;
|
||||
mRectListHead.width = mRectListHead.height = -100; // This is dummy marker node
|
||||
mCurRect = &mRectListHead;
|
||||
mRectCount = 0;
|
||||
mBoundRect.SetRect (0, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
void nsRegion::Empty ()
|
||||
{
|
||||
mCurRect = mRectListHead.next;
|
||||
|
||||
while (mCurRect != &mRectListHead)
|
||||
{
|
||||
RgnRect* tmp = mCurRect;
|
||||
mCurRect = mCurRect->next;
|
||||
delete tmp;
|
||||
}
|
||||
|
||||
mRectCount = 0;
|
||||
mRectListHead.prev = mRectListHead.next = &mRectListHead;
|
||||
mBoundRect.SetRect (0, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
// Insert node in right place of sorted list
|
||||
// If necessary then bounding rectangle could be updated and rectangle combined
|
||||
// with neighbour rectangles. This is usually done in Optimize ()
|
||||
|
||||
void nsRegion::InsertInPlace (RgnRect* aRect, PRBool aOptimizeOnFly)
|
||||
{
|
||||
if (mRectCount == 0)
|
||||
InsertAfter (aRect, &mRectListHead);
|
||||
else
|
||||
{
|
||||
if (aRect->y > mCurRect->y)
|
||||
{
|
||||
mRectListHead.y = 2147483647;
|
||||
|
||||
while (aRect->y > mCurRect->next->y)
|
||||
mCurRect = mCurRect->next;
|
||||
|
||||
while (aRect->y == mCurRect->next->y && aRect->x > mCurRect->next->x)
|
||||
mCurRect = mCurRect->next;
|
||||
|
||||
InsertAfter (aRect, mCurRect);
|
||||
} else
|
||||
if (aRect->y < mCurRect->y)
|
||||
{
|
||||
mRectListHead.y = -2147483647;
|
||||
|
||||
while (aRect->y < mCurRect->prev->y)
|
||||
mCurRect = mCurRect->prev;
|
||||
|
||||
while (aRect->y == mCurRect->prev->y && aRect->x < mCurRect->prev->x)
|
||||
mCurRect = mCurRect->prev;
|
||||
|
||||
InsertBefore (aRect, mCurRect);
|
||||
} else
|
||||
{
|
||||
if (aRect->x > mCurRect->x)
|
||||
{
|
||||
mRectListHead.y = 2147483647;
|
||||
|
||||
while (aRect->y == mCurRect->next->y && aRect->x > mCurRect->next->x)
|
||||
mCurRect = mCurRect->next;
|
||||
|
||||
InsertAfter (aRect, mCurRect);
|
||||
} else
|
||||
{
|
||||
mRectListHead.y = -2147483647;
|
||||
|
||||
while (aRect->y == mCurRect->prev->y && aRect->x < mCurRect->prev->x)
|
||||
mCurRect = mCurRect->prev;
|
||||
|
||||
InsertBefore (aRect, mCurRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (aOptimizeOnFly)
|
||||
{
|
||||
if (mRectCount == 1)
|
||||
mBoundRect = *mCurRect;
|
||||
else
|
||||
{
|
||||
mBoundRect.UnionRect (mBoundRect, *mCurRect);
|
||||
|
||||
// Check if we can go left or up before starting to combine rectangles
|
||||
if ((mCurRect->y == mCurRect->prev->y && mCurRect->height == mCurRect->prev->height &&
|
||||
mCurRect->x == mCurRect->prev->XMost ()) ||
|
||||
(mCurRect->x == mCurRect->prev->x && mCurRect->width == mCurRect->prev->width &&
|
||||
mCurRect->y == mCurRect->prev->YMost ()) )
|
||||
mCurRect = mCurRect->prev;
|
||||
|
||||
// Try to combine with rectangle on right side
|
||||
while (mCurRect->y == mCurRect->next->y && mCurRect->height == mCurRect->next->height &&
|
||||
mCurRect->XMost () == mCurRect->next->x)
|
||||
{
|
||||
mCurRect->width += mCurRect->next->width;
|
||||
delete Remove (mCurRect->next);
|
||||
}
|
||||
|
||||
// Try to combine with rectangle under this one
|
||||
while (mCurRect->x == mCurRect->next->x && mCurRect->width == mCurRect->next->width &&
|
||||
mCurRect->YMost () == mCurRect->next->y)
|
||||
{
|
||||
mCurRect->height += mCurRect->next->height;
|
||||
delete Remove (mCurRect->next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
nsRegion::RgnRect* nsRegion::Remove (RgnRect* aRect)
|
||||
{
|
||||
aRect->prev->next = aRect->next;
|
||||
aRect->next->prev = aRect->prev;
|
||||
mRectCount--;
|
||||
|
||||
if (mCurRect == aRect)
|
||||
mCurRect = (aRect->next != &mRectListHead) ? aRect->next : aRect->prev;
|
||||
|
||||
return aRect;
|
||||
}
|
||||
|
||||
|
||||
// Try to reduce the number of rectangles in complex region by combining with
|
||||
// surrounding ones on right and bottom sides of each rectangle in list.
|
||||
// Update bounding rectangle
|
||||
|
||||
void nsRegion::Optimize ()
|
||||
{
|
||||
mBoundRect.SetRect (0, 0, 0, 0);
|
||||
RgnRect* pRect = mRectListHead.next;
|
||||
|
||||
while (pRect != &mRectListHead)
|
||||
{
|
||||
// Try to combine with rectangle on right side
|
||||
while (pRect->y == pRect->next->y && pRect->height == pRect->next->height &&
|
||||
pRect->XMost () == pRect->next->x)
|
||||
{
|
||||
pRect->width += pRect->next->width;
|
||||
delete Remove (pRect->next);
|
||||
}
|
||||
|
||||
// Try to combine with rectangle under this one
|
||||
while (pRect->x == pRect->next->x && pRect->width == pRect->next->width &&
|
||||
pRect->YMost () == pRect->next->y)
|
||||
{
|
||||
pRect->height += pRect->next->height;
|
||||
delete Remove (pRect->next);
|
||||
}
|
||||
|
||||
mBoundRect.UnionRect (mBoundRect, *pRect);
|
||||
pRect = pRect->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Merge two non-overlapping regions into one.
|
||||
|
||||
void nsRegion::Merge (const nsRegion& aRgn1, const nsRegion& aRgn2)
|
||||
{
|
||||
if (aRgn1.mRectCount == 0) // Region empty. Result is equal to other region
|
||||
Copy (aRgn2);
|
||||
else
|
||||
if (aRgn2.mRectCount == 0) // Region empty. Result is equal to other region
|
||||
Copy (aRgn1);
|
||||
else
|
||||
{
|
||||
const nsRegion* pCopyRegion, *pInsertRegion;
|
||||
|
||||
// Determine which region contains more rectangles. Copy the larger one
|
||||
if (aRgn1.mRectCount >= aRgn2.mRectCount)
|
||||
{
|
||||
pCopyRegion = &aRgn1;
|
||||
pInsertRegion = &aRgn2;
|
||||
} else
|
||||
{
|
||||
pCopyRegion = &aRgn2;
|
||||
pInsertRegion = &aRgn1;
|
||||
}
|
||||
|
||||
if (pInsertRegion == this) // Do merge in-place
|
||||
pInsertRegion = pCopyRegion;
|
||||
else
|
||||
Copy (*pCopyRegion);
|
||||
|
||||
const RgnRect* pSrcRect = pInsertRegion->mRectListHead.next;
|
||||
|
||||
while (pSrcRect != &pInsertRegion->mRectListHead)
|
||||
{
|
||||
InsertInPlace (new RgnRect (*pSrcRect));
|
||||
|
||||
pSrcRect = pSrcRect->next;
|
||||
}
|
||||
|
||||
Optimize ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Copy (const nsRegion& aRegion)
|
||||
{
|
||||
if (&aRegion == this)
|
||||
return *this;
|
||||
|
||||
if (aRegion.mRectCount == 0)
|
||||
Empty ();
|
||||
else
|
||||
{
|
||||
while (mRectCount < aRegion.mRectCount) // Less rectangles than in source. Add missing ones
|
||||
InsertAfter (new RgnRect, &mRectListHead);
|
||||
|
||||
while (mRectCount > aRegion.mRectCount) // More rectangles than in source. Remove unnecessary
|
||||
delete Remove (mRectListHead.next);
|
||||
|
||||
const RgnRect* pSrc = aRegion.mRectListHead.next;
|
||||
RgnRect* pDest = mRectListHead.next;
|
||||
|
||||
while (pSrc != &aRegion.mRectListHead)
|
||||
{
|
||||
*pDest = *pSrc;
|
||||
|
||||
pSrc = pSrc->next;
|
||||
pDest = pDest->next;
|
||||
}
|
||||
|
||||
mCurRect = mRectListHead.next;
|
||||
mBoundRect = aRegion.mBoundRect;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Copy (const nsRect& aRect)
|
||||
{
|
||||
Empty ();
|
||||
|
||||
if (!aRect.IsEmpty ())
|
||||
{
|
||||
InsertAfter (new RgnRect (aRect), &mRectListHead);
|
||||
mBoundRect = aRect;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::And (const nsRegion& aRgn1, const nsRegion& aRgn2)
|
||||
{
|
||||
if (&aRgn1 == &aRgn2) // And with self
|
||||
Copy (aRgn1);
|
||||
else
|
||||
if (aRgn1.mRectCount == 0 || aRgn2.mRectCount == 0) // If either region is empty then result is empty
|
||||
Empty ();
|
||||
else
|
||||
{
|
||||
nsRect TmpRect;
|
||||
|
||||
if (aRgn1.mRectCount == 1 && aRgn2.mRectCount == 1) // Intersect rectangle with rectangle
|
||||
{
|
||||
TmpRect.IntersectRect (*aRgn1.mRectListHead.next, *aRgn2.mRectListHead.next);
|
||||
Copy (TmpRect);
|
||||
} else // Intersect region with region
|
||||
{
|
||||
nsRect BoundRect1, BoundRect2;
|
||||
aRgn1.GetBoundRect (&BoundRect1);
|
||||
aRgn2.GetBoundRect (&BoundRect2);
|
||||
|
||||
if (!TmpRect.IntersectRect (BoundRect1, BoundRect2)) // Regions do not intersect
|
||||
Empty ();
|
||||
else
|
||||
{
|
||||
nsRegion TmpRegion;
|
||||
const RgnRect* pSrcRect1 = aRgn1.mRectListHead.next;
|
||||
|
||||
while (pSrcRect1 != &aRgn1.mRectListHead)
|
||||
{
|
||||
const RgnRect* pSrcRect2 = aRgn2.mRectListHead.next;
|
||||
|
||||
while (pSrcRect2 != &aRgn2.mRectListHead)
|
||||
{
|
||||
if (TmpRect.IntersectRect (*pSrcRect1, *pSrcRect2))
|
||||
TmpRegion.InsertInPlace (new RgnRect (TmpRect));
|
||||
|
||||
pSrcRect2 = pSrcRect2->next;
|
||||
}
|
||||
|
||||
pSrcRect1 = pSrcRect1->next;
|
||||
}
|
||||
|
||||
TmpRegion.Optimize ();
|
||||
Copy (TmpRegion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::And (const nsRegion& aRegion, const nsRect& aRect)
|
||||
{
|
||||
// If either region or rectangle is empty then result is empty
|
||||
if (aRegion.mRectCount == 0 || aRect.IsEmpty ())
|
||||
Empty ();
|
||||
else // Intersect region with rectangle
|
||||
{
|
||||
nsRect TmpRect;
|
||||
|
||||
if (aRegion.mRectCount == 1) // Intersect rectangle with rectangle
|
||||
{
|
||||
TmpRect.IntersectRect (*aRegion.mRectListHead.next, aRect);
|
||||
Copy (TmpRect);
|
||||
} else // Intersect complex region with rectangle
|
||||
{
|
||||
aRegion.GetBoundRect (&TmpRect);
|
||||
|
||||
if (!TmpRect.IntersectRect (TmpRect, aRect)) // Rectangle does not intersect region
|
||||
Empty ();
|
||||
else
|
||||
{
|
||||
nsRegion TmpRegion;
|
||||
const RgnRect* pSrcRect = aRegion.mRectListHead.next;
|
||||
|
||||
while (pSrcRect != &aRegion.mRectListHead)
|
||||
{
|
||||
if (TmpRect.IntersectRect (*pSrcRect, aRect))
|
||||
TmpRegion.InsertInPlace (new RgnRect (TmpRect));
|
||||
|
||||
pSrcRect = pSrcRect->next;
|
||||
}
|
||||
|
||||
TmpRegion.Optimize ();
|
||||
Copy (TmpRegion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Or (const nsRegion& aRgn1, const nsRegion& aRgn2)
|
||||
{
|
||||
if (&aRgn1 == &aRgn2) // Or with self
|
||||
Copy (aRgn1);
|
||||
else
|
||||
if (aRgn1.mRectCount == 0) // Region empty. Result is equal to other region
|
||||
Copy (aRgn2);
|
||||
else
|
||||
if (aRgn2.mRectCount == 0) // Region empty. Result is equal to other region
|
||||
Copy (aRgn1);
|
||||
else
|
||||
{
|
||||
nsRect BoundRect1, BoundRect2;
|
||||
aRgn1.GetBoundRect (&BoundRect1);
|
||||
aRgn2.GetBoundRect (&BoundRect2);
|
||||
|
||||
if (!BoundRect1.IntersectRect (BoundRect1, BoundRect2)) // Regions do not intersect
|
||||
{
|
||||
Merge (aRgn1, aRgn2);
|
||||
Optimize ();
|
||||
} else
|
||||
{
|
||||
nsRegion TmpRegion;
|
||||
TmpRegion.Sub (aRgn1, aRgn2); // Get only parts of region which not overlap the other region
|
||||
Merge (TmpRegion, aRgn2);
|
||||
Optimize ();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Or (const nsRegion& aRegion, const nsRect& aRect)
|
||||
{
|
||||
if (aRegion.mRectCount == 0) // Region empty. Result is equal to rectangle
|
||||
Copy (aRect);
|
||||
else
|
||||
if (aRect.IsEmpty ()) // Rectangle is empty. Result is equal to region
|
||||
Copy (aRegion);
|
||||
else
|
||||
{
|
||||
nsRect BoundRect, TmpRect;
|
||||
aRegion.GetBoundRect (&BoundRect);
|
||||
|
||||
if (!TmpRect.IntersectRect (BoundRect, aRect)) // Rectangle does not intersect region
|
||||
{
|
||||
Copy (aRegion);
|
||||
InsertInPlace (new RgnRect (aRect), PR_TRUE);
|
||||
} else
|
||||
{
|
||||
if (aRect.Contains (BoundRect)) // Rectangle fully overlays region
|
||||
Copy (aRect);
|
||||
else
|
||||
{
|
||||
SubRectFromRegion (aRegion, aRect); // Exclude from region parts that overlap the rectangle
|
||||
InsertInPlace (new RgnRect (aRect));
|
||||
Optimize ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Xor (const nsRegion& aRgn1, const nsRegion& aRgn2)
|
||||
{
|
||||
if (&aRgn1 == &aRgn2) // Xor with self
|
||||
Empty ();
|
||||
else
|
||||
if (aRgn1.mRectCount == 0) // Region empty. Result is equal to other region
|
||||
Copy (aRgn2);
|
||||
else
|
||||
if (aRgn2.mRectCount == 0) // Region empty. Result is equal to other region
|
||||
Copy (aRgn1);
|
||||
else
|
||||
{
|
||||
nsRect BoundRect1, BoundRect2;
|
||||
aRgn1.GetBoundRect (&BoundRect1);
|
||||
aRgn2.GetBoundRect (&BoundRect2);
|
||||
|
||||
if (!BoundRect1.IntersectRect (BoundRect1, BoundRect2)) // Regions do not intersect
|
||||
{
|
||||
Merge (aRgn1, aRgn2);
|
||||
Optimize ();
|
||||
} else
|
||||
{
|
||||
nsRegion TmpRegion1, TmpRegion2;
|
||||
TmpRegion2.And (aRgn1, aRgn2); // Overlay
|
||||
TmpRegion1.Sub (aRgn1, TmpRegion2); // aRgn1 - Overlay
|
||||
TmpRegion2.Sub (aRgn2, TmpRegion2); // aRgn2 - Overlay
|
||||
Merge (TmpRegion1, TmpRegion2);
|
||||
Optimize ();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Xor (const nsRegion& aRegion, const nsRect& aRect)
|
||||
{
|
||||
if (aRegion.mRectCount == 0) // Region empty. Result is equal to rectangle
|
||||
Copy (aRect);
|
||||
else
|
||||
if (aRect.IsEmpty ()) // Rectangle is empty. Result is equal to region
|
||||
Copy (aRegion);
|
||||
else
|
||||
{
|
||||
nsRect BoundRect;
|
||||
aRegion.GetBoundRect (&BoundRect);
|
||||
|
||||
if (!BoundRect.IntersectRect (BoundRect, aRect)) // Rectangle does not intersect region
|
||||
{
|
||||
Copy (aRegion);
|
||||
InsertInPlace (new RgnRect (aRect), PR_TRUE);
|
||||
} else
|
||||
{
|
||||
nsRegion IntersectRegion, TmpRegion1, TmpRegion2;
|
||||
IntersectRegion.And (aRegion, aRect);
|
||||
TmpRegion1.Sub (aRegion, IntersectRegion);
|
||||
TmpRegion2.Copy (aRect);
|
||||
TmpRegion2.Sub (TmpRegion2, IntersectRegion);
|
||||
Merge (TmpRegion1, TmpRegion2);
|
||||
Optimize ();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Sub (const nsRegion& aRgn1, const nsRegion& aRgn2)
|
||||
{
|
||||
if (&aRgn1 == &aRgn2) // Sub from self
|
||||
Empty ();
|
||||
else
|
||||
if (aRgn1.mRectCount == 0) // If source is empty then result is empty, too
|
||||
Empty ();
|
||||
else
|
||||
if (aRgn2.mRectCount == 0) // Nothing to subtract
|
||||
Copy (aRgn1);
|
||||
else
|
||||
{
|
||||
nsRect BoundRect1, BoundRect2;
|
||||
aRgn1.GetBoundRect (&BoundRect1);
|
||||
aRgn2.GetBoundRect (&BoundRect2);
|
||||
|
||||
if (!BoundRect1.IntersectRect (BoundRect1, BoundRect2)) // Regions do not intersect
|
||||
Copy (aRgn1);
|
||||
else
|
||||
{
|
||||
nsRegion TmpRegion;
|
||||
const RgnRect* pRect = aRgn2.mRectListHead.next;
|
||||
|
||||
TmpRegion.SubRectFromRegion (aRgn1, *pRect);
|
||||
pRect = pRect->next;
|
||||
|
||||
while (pRect != &aRgn2.mRectListHead)
|
||||
{
|
||||
TmpRegion.SubRectFromRegion (TmpRegion, *pRect);
|
||||
pRect = pRect->next;
|
||||
}
|
||||
|
||||
TmpRegion.Optimize ();
|
||||
Copy (TmpRegion);
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsRegion& nsRegion::Sub (const nsRegion& aRegion, const nsRect& aRect)
|
||||
{
|
||||
if (aRegion.mRectCount == 0) // If source is empty then result is empty, too
|
||||
Empty ();
|
||||
else
|
||||
if (aRect.IsEmpty ()) // Nothing to subtract
|
||||
Copy (aRegion);
|
||||
else
|
||||
{
|
||||
nsRect BoundRect, TmpRect;
|
||||
aRegion.GetBoundRect (&BoundRect);
|
||||
|
||||
if (!TmpRect.IntersectRect (BoundRect, aRect)) // Rectangle does not intersect region
|
||||
Copy (aRegion);
|
||||
else
|
||||
{
|
||||
if (aRect.Contains (BoundRect)) // Rectangle fully overlays region
|
||||
Empty ();
|
||||
else
|
||||
{
|
||||
SubRectFromRegion (aRegion, aRect);
|
||||
Optimize ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void nsRegion::SubRectFromRegion (const nsRegion& aRegion, const nsRect& aRect)
|
||||
{
|
||||
nsRegion TmpRegion;
|
||||
TmpRegion.Copy (aRegion);
|
||||
Empty ();
|
||||
|
||||
const RgnRect* pSrcRect = TmpRegion.mRectListHead.next;
|
||||
|
||||
while (pSrcRect != &TmpRegion.mRectListHead)
|
||||
{
|
||||
nsRect TmpRect;
|
||||
|
||||
if (!TmpRect.IntersectRect (*pSrcRect, aRect))
|
||||
InsertInPlace (new RgnRect (*pSrcRect));
|
||||
else
|
||||
{
|
||||
// Rectangle A. Subtract from this rectangle B
|
||||
const nscoord ax = pSrcRect->x;
|
||||
const nscoord axm = pSrcRect->XMost ();
|
||||
const nscoord aw = pSrcRect->width;
|
||||
const nscoord ay = pSrcRect->y;
|
||||
const nscoord aym = pSrcRect->YMost ();
|
||||
const nscoord ah = pSrcRect->height;
|
||||
// Rectangle B. Subtract this from rectangle A
|
||||
const nscoord bx = aRect.x;
|
||||
const nscoord bxm = aRect.XMost ();
|
||||
const nscoord bw = aRect.width;
|
||||
const nscoord by = aRect.y;
|
||||
const nscoord bym = aRect.YMost ();
|
||||
const nscoord bh = aRect.height;
|
||||
// Rectangle I. Area where rectangles A and B intersect
|
||||
const nscoord ix = TmpRect.x;
|
||||
const nscoord ixm = TmpRect.XMost ();
|
||||
const nscoord iw = TmpRect.width;
|
||||
const nscoord iy = TmpRect.y;
|
||||
const nscoord iym = TmpRect.YMost ();
|
||||
const nscoord ih = TmpRect.height;
|
||||
|
||||
// There are 16 combinations how rectangles could intersect
|
||||
|
||||
if (bx <= ax && by <= ay)
|
||||
{
|
||||
if (bxm < axm && bym < aym) // 1.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ixm, ay, axm - ixm, ih));
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm >= axm && bym < aym) // 2.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm < axm && bym >= aym) // 3.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ixm, ay, axm - ixm, ah));
|
||||
}
|
||||
} else
|
||||
if (bx > ax && by <= ay)
|
||||
{
|
||||
if (bxm < axm && bym < aym) // 5.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, ix - ax, ih));
|
||||
InsertInPlace (new RgnRect (ixm, ay, axm - ixm, ih));
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm >= axm && bym < aym) // 6.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, ix - ax, ih));
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm < axm && bym >= aym) // 7.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, ix - ax, ah));
|
||||
InsertInPlace (new RgnRect (ixm, ay, axm - ixm, ah));
|
||||
} else
|
||||
if (bxm >= axm && bym >= aym) // 8.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, ix - ax, ah));
|
||||
}
|
||||
} else
|
||||
if (bx <= ax && by > ay)
|
||||
{
|
||||
if (bxm < axm && bym < aym) // 9.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
InsertInPlace (new RgnRect (ixm, iy, axm - ixm, ih));
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm >= axm && bym < aym) // 10.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm < axm && bym >= aym) // 11.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
InsertInPlace (new RgnRect (ixm, iy, axm - ixm, ih));
|
||||
} else
|
||||
if (bxm >= axm && bym >= aym) // 12.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
}
|
||||
} else
|
||||
if (bx > ax && by > ay)
|
||||
{
|
||||
if (bxm < axm && bym < aym) // 13.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
InsertInPlace (new RgnRect (ax, iy, ix - ax, ih));
|
||||
InsertInPlace (new RgnRect (ixm, iy, axm - ixm, ih));
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm >= axm && bym < aym) // 14.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
InsertInPlace (new RgnRect (ax, iy, ix - ax, ih));
|
||||
InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
|
||||
} else
|
||||
if (bxm < axm && bym >= aym) // 15.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
InsertInPlace (new RgnRect (ax, iy, ix - ax, ih));
|
||||
InsertInPlace (new RgnRect (ixm, iy, axm - ixm, ih));
|
||||
} else
|
||||
if (bxm >= axm && bym >= aym) // 16.
|
||||
{
|
||||
InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
|
||||
InsertInPlace (new RgnRect (ax, iy, ix - ax, ih));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pSrcRect = pSrcRect->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PRBool nsRegion::IsEqual (const nsRegion& aRegion) const
|
||||
{
|
||||
if (mRectCount == 0)
|
||||
return (aRegion.mRectCount == 0) ? PR_TRUE : PR_FALSE;
|
||||
|
||||
if (aRegion.mRectCount == 0)
|
||||
return (mRectCount == 0) ? PR_TRUE : PR_FALSE;
|
||||
|
||||
if (mRectCount == 1 && aRegion.mRectCount == 1) // Both regions are simple rectangles
|
||||
{
|
||||
return (*mRectListHead.next == *aRegion.mRectListHead.next);
|
||||
} else // At least one is complex region.
|
||||
{
|
||||
nsRegion TmpRegion;
|
||||
TmpRegion.Xor (*this, aRegion); // Get difference between two regions
|
||||
|
||||
return (TmpRegion.mRectCount == 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void nsRegion::Offset (PRInt32 aXOffset, PRInt32 aYOffset)
|
||||
{
|
||||
if (aXOffset || aYOffset)
|
||||
{
|
||||
RgnRect* pRect = mRectListHead.next;
|
||||
|
||||
while (pRect != &mRectListHead)
|
||||
{
|
||||
pRect->x += aXOffset;
|
||||
pRect->y += aYOffset;
|
||||
|
||||
pRect = pRect->next;
|
||||
}
|
||||
|
||||
mBoundRect.x += aXOffset;
|
||||
mBoundRect.y += aYOffset;
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче