2
* The contents of this file are subject to the Mozilla Public
3
* License Version 1.1 (the "License"); you may not use this file
4
* except in compliance with the License. You may obtain a copy of
5
* the License at http://www.mozilla.org/MPL/
7
* Software distributed under the License is distributed on an "AS
8
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9
* implied. See the License for the specific language governing
10
* rights and limitations under the License.
12
* The Original Code is mozilla.org code.
14
* The Initial Developer of the Original Code is Dainis Jonitis,
15
* <Dainis_Jonitis@swh-t.lv>. Portions created by Dainis Jonitis are
16
* Copyright (C) 2001 Dainis Jonitis. All Rights Reserved.
23
#include "nsISupportsImpl.h"
26
#define MIN_INT32 (-PR_INT32 (0x7FFFFFFF) - 1)
27
#define MAX_INT32 (PR_INT32 (0x7FFFFFFF))
30
// Fast inline analogues of nsRect methods for nsRegion::nsRectFast.
31
// Check for emptiness is not required - it is guaranteed by caller.
33
inline PRBool nsRegion::nsRectFast::Contains (const nsRect& aRect) const
35
return (PRBool) ((aRect.x >= x) && (aRect.y >= y) &&
36
(aRect.XMost () <= XMost ()) && (aRect.YMost () <= YMost ()));
39
inline PRBool nsRegion::nsRectFast::Intersects (const nsRect& aRect) const
41
return (PRBool) ((x < aRect.XMost ()) && (y < aRect.YMost ()) &&
42
(aRect.x < XMost ()) && (aRect.y < YMost ()));
45
inline PRBool nsRegion::nsRectFast::IntersectRect (const nsRect& aRect1, const nsRect& aRect2)
47
const nscoord xmost = PR_MIN (aRect1.XMost (), aRect2.XMost ());
48
x = PR_MAX (aRect1.x, aRect2.x);
50
if (width <= 0) return PR_FALSE;
52
const nscoord ymost = PR_MIN (aRect1.YMost (), aRect2.YMost ());
53
y = PR_MAX (aRect1.y, aRect2.y);
55
if (height <= 0) return PR_FALSE;
60
inline void nsRegion::nsRectFast::UnionRect (const nsRect& aRect1, const nsRect& aRect2)
62
const nscoord xmost = PR_MAX (aRect1.XMost (), aRect2.XMost ());
63
const nscoord ymost = PR_MAX (aRect1.YMost (), aRect2.YMost ());
64
x = PR_MIN (aRect1.x, aRect2.x);
65
y = PR_MIN (aRect1.y, aRect2.y);
72
// Custom memory allocator for nsRegion::RgnRect structures.
73
// Entries are allocated from global memory pool.
74
// Memory pool can grow in size, but it can't shrink.
76
#define INIT_MEM_CHUNK_ENTRIES 100
77
#define INCR_MEM_CHUNK_ENTRIES 100
79
class RgnRectMemoryAllocator
81
nsRegion::RgnRect* mFreeListHead;
82
PRUint32 mFreeEntries;
87
void InitLock () { mLock = PR_NewLock (); }
88
void DestroyLock () { PR_DestroyLock (mLock); }
89
void Lock () { PR_Lock (mLock); }
90
void Unlock () { PR_Unlock (mLock); }
94
void InitLock () { NS_ASSERT_OWNINGTHREAD (RgnRectMemoryAllocator); }
95
void DestroyLock () { NS_ASSERT_OWNINGTHREAD (RgnRectMemoryAllocator); }
96
void Lock () { NS_ASSERT_OWNINGTHREAD (RgnRectMemoryAllocator); }
97
void Unlock () { NS_ASSERT_OWNINGTHREAD (RgnRectMemoryAllocator); }
100
void DestroyLock () { }
105
void* AllocChunk (PRUint32 aEntries, void* aNextChunk, nsRegion::RgnRect* aTailDest)
107
PRUint8* pBuf = new PRUint8 [aEntries * sizeof (nsRegion::RgnRect) + sizeof (void*)];
108
*NS_REINTERPRET_CAST (void**, pBuf) = aNextChunk;
109
nsRegion::RgnRect* pRect = NS_REINTERPRET_CAST (nsRegion::RgnRect*, pBuf + sizeof (void*));
111
for (PRUint32 cnt = 0 ; cnt < aEntries - 1 ; cnt++)
112
pRect [cnt].next = &pRect [cnt + 1];
114
pRect [aEntries - 1].next = aTailDest;
119
void FreeChunk (void* aChunk) { delete [] (PRUint8 *) aChunk; }
120
void* NextChunk (void* aThisChunk) const { return *NS_STATIC_CAST (void**, aThisChunk); }
122
nsRegion::RgnRect* ChunkHead (void* aThisChunk) const
123
{ return NS_REINTERPRET_CAST (nsRegion::RgnRect*, NS_STATIC_CAST (PRUint8*, aThisChunk) + sizeof (void*)); }
126
RgnRectMemoryAllocator (PRUint32 aNumOfEntries);
127
~RgnRectMemoryAllocator ();
129
nsRegion::RgnRect* Alloc ();
130
void Free (nsRegion::RgnRect* aRect);
134
RgnRectMemoryAllocator::RgnRectMemoryAllocator (PRUint32 aNumOfEntries)
137
mChunkListHead = AllocChunk (aNumOfEntries, nsnull, nsnull);
138
mFreeEntries = aNumOfEntries;
139
mFreeListHead = ChunkHead (mChunkListHead);
142
RgnRectMemoryAllocator::~RgnRectMemoryAllocator ()
144
while (mChunkListHead)
146
void* tmp = mChunkListHead;
147
mChunkListHead = NextChunk (mChunkListHead);
153
* As a static object this class outlives any library which would implement
154
* locking. So we intentionally leak the 'lock'.
156
* Currently RgnRectMemoryAllocator is only used from the primary thread,
157
* so we aren't using a lock which means that there is no lock to leak.
158
* If we ever switch to multiple GUI threads (e.g. one per window),
159
* we'd probably use one allocator per window-thread to avoid the
160
* locking overhead and just require consumers not to pass regions
161
* across threads/windows, which would be a reasonable restriction
162
* because they wouldn't be useful outside their window.
168
nsRegion::RgnRect* RgnRectMemoryAllocator::Alloc ()
172
if (mFreeEntries == 0)
174
mChunkListHead = AllocChunk (INCR_MEM_CHUNK_ENTRIES, mChunkListHead, mFreeListHead);
175
mFreeEntries = INCR_MEM_CHUNK_ENTRIES;
176
mFreeListHead = ChunkHead (mChunkListHead);
179
nsRegion::RgnRect* tmp = mFreeListHead;
180
mFreeListHead = mFreeListHead->next;
187
void RgnRectMemoryAllocator::Free (nsRegion::RgnRect* aRect)
191
aRect->next = mFreeListHead;
192
mFreeListHead = aRect;
197
// Global pool for nsRegion::RgnRect allocation
198
static RgnRectMemoryAllocator gRectPool (INIT_MEM_CHUNK_ENTRIES);
201
inline void* nsRegion::RgnRect::operator new (size_t) CPP_THROW_NEW
203
return gRectPool.Alloc ();
206
inline void nsRegion::RgnRect::operator delete (void* aRect, size_t)
208
gRectPool.Free (NS_STATIC_CAST (RgnRect*, aRect));
213
void nsRegion::Init()
215
mRectListHead.prev = mRectListHead.next = &mRectListHead;
216
mCurRect = &mRectListHead;
218
mBoundRect.SetRect (0, 0, 0, 0);
221
inline void nsRegion::InsertBefore (RgnRect* aNewRect, RgnRect* aRelativeRect)
223
aNewRect->prev = aRelativeRect->prev;
224
aNewRect->next = aRelativeRect;
225
aRelativeRect->prev->next = aNewRect;
226
aRelativeRect->prev = aNewRect;
231
inline void nsRegion::InsertAfter (RgnRect* aNewRect, RgnRect* aRelativeRect)
233
aNewRect->prev = aRelativeRect;
234
aNewRect->next = aRelativeRect->next;
235
aRelativeRect->next->prev = aNewRect;
236
aRelativeRect->next = aNewRect;
242
// Adjust the number of rectangles in region.
243
// Content of rectangles should be changed by caller.
245
void nsRegion::SetToElements (PRUint32 aCount)
247
if (mRectCount < aCount) // Add missing rectangles
249
PRUint32 InsertCount = aCount - mRectCount;
251
RgnRect* pPrev = &mRectListHead;
252
RgnRect* pNext = mRectListHead.next;
254
while (InsertCount--)
256
mCurRect = new RgnRect;
257
mCurRect->prev = pPrev;
258
pPrev->next = mCurRect;
265
if (mRectCount > aCount) // Remove unnecessary rectangles
267
PRUint32 RemoveCount = mRectCount - aCount;
269
mCurRect = mRectListHead.next;
271
while (RemoveCount--)
273
RgnRect* tmp = mCurRect;
274
mCurRect = mCurRect->next;
278
mRectListHead.next = mCurRect;
279
mCurRect->prev = &mRectListHead;
284
// Save the entire chain of linked elements in 'prev' field of the RgnRect structure.
285
// After that forward-only iterations using 'next' field could still be used.
286
// Some elements from forward-only chain could be temporarily removed to optimize inner loops.
287
// The original double linked state could be restored by call to RestoreLinkChain ().
288
// Both functions despite size can be inline because they are called only from one function.
290
inline void nsRegion::SaveLinkChain ()
292
RgnRect* pRect = &mRectListHead;
296
pRect->prev = pRect->next;
298
} while (pRect != &mRectListHead);
302
inline void nsRegion::RestoreLinkChain ()
304
RgnRect* pPrev = &mRectListHead;
305
RgnRect* pRect = mRectListHead.next = mRectListHead.prev;
307
while (pRect != &mRectListHead)
309
pRect->next = pRect->prev;
315
mRectListHead.prev = pPrev;
319
// Insert node in right place of sorted list
320
// If necessary then bounding rectangle could be updated and rectangle combined
321
// with neighbour rectangles. This is usually done in Optimize ()
323
void nsRegion::InsertInPlace (RgnRect* aRect, PRBool aOptimizeOnFly)
326
InsertAfter (aRect, &mRectListHead);
329
if (aRect->y > mCurRect->y)
331
mRectListHead.y = MAX_INT32;
333
while (aRect->y > mCurRect->next->y)
334
mCurRect = mCurRect->next;
336
while (aRect->y == mCurRect->next->y && aRect->x > mCurRect->next->x)
337
mCurRect = mCurRect->next;
339
InsertAfter (aRect, mCurRect);
341
if (aRect->y < mCurRect->y)
343
mRectListHead.y = MIN_INT32;
345
while (aRect->y < mCurRect->prev->y)
346
mCurRect = mCurRect->prev;
348
while (aRect->y == mCurRect->prev->y && aRect->x < mCurRect->prev->x)
349
mCurRect = mCurRect->prev;
351
InsertBefore (aRect, mCurRect);
354
if (aRect->x > mCurRect->x)
356
mRectListHead.y = MAX_INT32;
358
while (aRect->y == mCurRect->next->y && aRect->x > mCurRect->next->x)
359
mCurRect = mCurRect->next;
361
InsertAfter (aRect, mCurRect);
364
mRectListHead.y = MIN_INT32;
366
while (aRect->y == mCurRect->prev->y && aRect->x < mCurRect->prev->x)
367
mCurRect = mCurRect->prev;
369
InsertBefore (aRect, mCurRect);
378
mBoundRect = *mCurRect;
381
mBoundRect.UnionRect (mBoundRect, *mCurRect);
383
// Check if we can go left or up before starting to combine rectangles
384
if ((mCurRect->y == mCurRect->prev->y && mCurRect->height == mCurRect->prev->height &&
385
mCurRect->x == mCurRect->prev->XMost ()) ||
386
(mCurRect->x == mCurRect->prev->x && mCurRect->width == mCurRect->prev->width &&
387
mCurRect->y == mCurRect->prev->YMost ()) )
388
mCurRect = mCurRect->prev;
390
// Try to combine with rectangle on right side
391
while (mCurRect->y == mCurRect->next->y && mCurRect->height == mCurRect->next->height &&
392
mCurRect->XMost () == mCurRect->next->x)
394
mCurRect->width += mCurRect->next->width;
395
delete Remove (mCurRect->next);
398
// Try to combine with rectangle under this one
399
while (mCurRect->x == mCurRect->next->x && mCurRect->width == mCurRect->next->width &&
400
mCurRect->YMost () == mCurRect->next->y)
402
mCurRect->height += mCurRect->next->height;
403
delete Remove (mCurRect->next);
410
nsRegion::RgnRect* nsRegion::Remove (RgnRect* aRect)
412
aRect->prev->next = aRect->next;
413
aRect->next->prev = aRect->prev;
416
if (mCurRect == aRect)
417
mCurRect = (aRect->next != &mRectListHead) ? aRect->next : aRect->prev;
423
// Try to reduce the number of rectangles in complex region by combining with
424
// surrounding ones on right and bottom sides of each rectangle in list.
425
// Update bounding rectangle
427
void nsRegion::Optimize ()
430
mBoundRect.SetRect (0, 0, 0, 0);
433
RgnRect* pRect = mRectListHead.next;
434
PRInt32 xmost = mRectListHead.prev->XMost ();
435
PRInt32 ymost = mRectListHead.prev->YMost ();
436
mBoundRect.x = mRectListHead.next->x;
437
mBoundRect.y = mRectListHead.next->y;
439
while (pRect != &mRectListHead)
441
// Try to combine with rectangle on right side
442
while (pRect->y == pRect->next->y && pRect->height == pRect->next->height &&
443
pRect->XMost () == pRect->next->x)
445
pRect->width += pRect->next->width;
446
delete Remove (pRect->next);
449
// Try to combine with rectangle under this one
450
while (pRect->x == pRect->next->x && pRect->width == pRect->next->width &&
451
pRect->YMost () == pRect->next->y)
453
pRect->height += pRect->next->height;
454
delete Remove (pRect->next);
457
// Determine bound rectangle. Use fact that rectangles are sorted.
458
if (pRect->x < mBoundRect.x) mBoundRect.x = pRect->x;
459
if (pRect->XMost () > xmost) xmost = pRect->XMost ();
460
if (pRect->YMost () > ymost) ymost = pRect->YMost ();
465
mBoundRect.width = xmost - mBoundRect.x;
466
mBoundRect.height = ymost - mBoundRect.y;
471
// Move rectangles starting from 'aStartRect' till end of the list to the destionation region.
472
// Important for temporary objects - instead of copying rectangles with Merge () and then
473
// emptying region in destructor they could be moved to destination region in one step.
475
void nsRegion::MoveInto (nsRegion& aDestRegion, const RgnRect* aStartRect)
477
RgnRect* pRect = NS_CONST_CAST (RgnRect*, aStartRect);
478
RgnRect* pPrev = pRect->prev;
480
while (pRect != &mRectListHead)
482
RgnRect* next = pRect->next;
483
aDestRegion.InsertInPlace (pRect);
489
pPrev->next = &mRectListHead;
490
mRectListHead.prev = pPrev;
491
mCurRect = mRectListHead.next;
495
// Merge two non-overlapping regions into one.
496
// Automatically optimize region by calling Optimize ()
498
void nsRegion::Merge (const nsRegion& aRgn1, const nsRegion& aRgn2)
500
if (aRgn1.mRectCount == 0) // Region empty. Result is equal to other region
503
if (aRgn2.mRectCount == 0) // Region empty. Result is equal to other region
505
if (aRgn1.mRectCount == 1) // Region is single rectangle. Optimize on fly
507
RgnRect* TmpRect = new RgnRect (*aRgn1.mRectListHead.next);
509
InsertInPlace (TmpRect, PR_TRUE);
511
if (aRgn2.mRectCount == 1) // Region is single rectangle. Optimize on fly
513
RgnRect* TmpRect = new RgnRect (*aRgn2.mRectListHead.next);
515
InsertInPlace (TmpRect, PR_TRUE);
518
const nsRegion* pCopyRegion, *pInsertRegion;
520
// Determine which region contains more rectangles. Copy the larger one
521
if (aRgn1.mRectCount >= aRgn2.mRectCount)
523
pCopyRegion = &aRgn1;
524
pInsertRegion = &aRgn2;
527
pCopyRegion = &aRgn2;
528
pInsertRegion = &aRgn1;
531
if (pInsertRegion == this) // Do merge in-place
532
pInsertRegion = pCopyRegion;
536
const RgnRect* pSrcRect = pInsertRegion->mRectListHead.next;
538
while (pSrcRect != &pInsertRegion->mRectListHead)
540
InsertInPlace (new RgnRect (*pSrcRect));
542
pSrcRect = pSrcRect->next;
550
nsRegion& nsRegion::Copy (const nsRegion& aRegion)
552
if (&aRegion == this)
555
if (aRegion.mRectCount == 0)
559
SetToElements (aRegion.mRectCount);
561
const RgnRect* pSrc = aRegion.mRectListHead.next;
562
RgnRect* pDest = mRectListHead.next;
564
while (pSrc != &aRegion.mRectListHead)
572
mCurRect = mRectListHead.next;
573
mBoundRect = aRegion.mBoundRect;
580
nsRegion& nsRegion::Copy (const nsRect& aRect)
582
if (aRect.IsEmpty ())
587
*mRectListHead.next = NS_STATIC_CAST (const RgnRect&, aRect);
588
mBoundRect = NS_STATIC_CAST (const nsRectFast&, aRect);
595
nsRegion& nsRegion::And (const nsRegion& aRgn1, const nsRegion& aRgn2)
597
if (&aRgn1 == &aRgn2) // And with self
600
if (aRgn1.mRectCount == 0 || aRgn2.mRectCount == 0) // If either region is empty then result is empty
606
if (aRgn1.mRectCount == 1 && aRgn2.mRectCount == 1) // Intersect rectangle with rectangle
608
TmpRect.IntersectRect (*aRgn1.mRectListHead.next, *aRgn2.mRectListHead.next);
612
if (!aRgn1.mBoundRect.Intersects (aRgn2.mBoundRect)) // Regions do not intersect
616
// Region is simple rectangle and it fully overlays other region
617
if (aRgn1.mRectCount == 1 && aRgn1.mBoundRect.Contains (aRgn2.mBoundRect))
620
// Region is simple rectangle and it fully overlays other region
621
if (aRgn2.mRectCount == 1 && aRgn2.mBoundRect.Contains (aRgn1.mBoundRect))
626
nsRegion* pSrcRgn1 = NS_CONST_CAST (nsRegion*, &aRgn1);
627
nsRegion* pSrcRgn2 = NS_CONST_CAST (nsRegion*, &aRgn2);
629
if (&aRgn1 == this) // Copy region if it is both source and result
631
TmpRegion.Copy (aRgn1);
632
pSrcRgn1 = &TmpRegion;
635
if (&aRgn2 == this) // Copy region if it is both source and result
637
TmpRegion.Copy (aRgn2);
638
pSrcRgn2 = &TmpRegion;
641
// For outer loop prefer region for which at least one rectangle is below other's bound rectangle
642
if (pSrcRgn2->mRectListHead.prev->y >= pSrcRgn1->mBoundRect.YMost ())
644
nsRegion* Tmp = pSrcRgn1;
651
pSrcRgn2->SaveLinkChain ();
653
pSrcRgn1->mRectListHead.y = MAX_INT32;
654
pSrcRgn2->mRectListHead.y = MAX_INT32;
656
for (RgnRect* pSrcRect1 = pSrcRgn1->mRectListHead.next ;
657
pSrcRect1->y < pSrcRgn2->mBoundRect.YMost () ; pSrcRect1 = pSrcRect1->next)
659
if (pSrcRect1->Intersects (pSrcRgn2->mBoundRect)) // Rectangle intersects region. Process each rectangle
661
RgnRect* pPrev2 = &pSrcRgn2->mRectListHead;
663
for (RgnRect* pSrcRect2 = pSrcRgn2->mRectListHead.next ;
664
pSrcRect2->y < pSrcRect1->YMost () ; pSrcRect2 = pSrcRect2->next)
666
if (pSrcRect2->YMost () <= pSrcRect1->y) // Rect2's bottom is above the top of Rect1.
667
{ // No successive rectangles in Rgn1 can intersect it.
668
pPrev2->next = pSrcRect2->next; // Remove Rect2 from Rgn2's checklist
672
if (pSrcRect1->Contains (*pSrcRect2)) // Rect1 fully overlays Rect2.
673
{ // No any other rectangle in Rgn1 can intersect it.
674
pPrev2->next = pSrcRect2->next; // Remove Rect2 from Rgn2's checklist
675
InsertInPlace (new RgnRect (*pSrcRect2));
680
if (TmpRect.IntersectRect (*pSrcRect1, *pSrcRect2))
681
InsertInPlace (new RgnRect (TmpRect));
688
pSrcRgn2->RestoreLinkChain ();
699
nsRegion& nsRegion::And (const nsRegion& aRegion, const nsRect& aRect)
701
// If either region or rectangle is empty then result is empty
702
if (aRegion.mRectCount == 0 || aRect.IsEmpty ())
704
else // Intersect region with rectangle
706
const nsRectFast& aRectFast = NS_STATIC_CAST (const nsRectFast&, aRect);
709
if (aRegion.mRectCount == 1) // Intersect rectangle with rectangle
711
TmpRect.IntersectRect (*aRegion.mRectListHead.next, aRectFast);
713
} else // Intersect complex region with rectangle
715
if (!aRectFast.Intersects (aRegion.mBoundRect)) // Rectangle does not intersect region
719
if (aRectFast.Contains (aRegion.mBoundRect)) // Rectangle fully overlays region
724
nsRegion* pSrcRegion = NS_CONST_CAST (nsRegion*, &aRegion);
726
if (&aRegion == this) // Copy region if it is both source and result
728
TmpRegion.Copy (aRegion);
729
pSrcRegion = &TmpRegion;
733
pSrcRegion->mRectListHead.y = MAX_INT32;
735
for (const RgnRect* pSrcRect = pSrcRegion->mRectListHead.next ;
736
pSrcRect->y < aRectFast.YMost () ; pSrcRect = pSrcRect->next)
738
if (TmpRect.IntersectRect (*pSrcRect, aRectFast))
739
InsertInPlace (new RgnRect (TmpRect));
752
nsRegion& nsRegion::Or (const nsRegion& aRgn1, const nsRegion& aRgn2)
754
if (&aRgn1 == &aRgn2) // Or with self
757
if (aRgn1.mRectCount == 0) // Region empty. Result is equal to other region
760
if (aRgn2.mRectCount == 0) // Region empty. Result is equal to other region
764
if (!aRgn1.mBoundRect.Intersects (aRgn2.mBoundRect)) // Regions do not intersect
765
Merge (aRgn1, aRgn2);
768
// Region is simple rectangle and it fully overlays other region
769
if (aRgn1.mRectCount == 1 && aRgn1.mBoundRect.Contains (aRgn2.mBoundRect))
772
// Region is simple rectangle and it fully overlays other region
773
if (aRgn2.mRectCount == 1 && aRgn2.mBoundRect.Contains (aRgn1.mBoundRect))
778
aRgn1.SubRegion (aRgn2, TmpRegion); // Get only parts of region which not overlap the other region
780
TmpRegion.MoveInto (*this);
790
nsRegion& nsRegion::Or (const nsRegion& aRegion, const nsRect& aRect)
792
if (aRegion.mRectCount == 0) // Region empty. Result is equal to rectangle
795
if (aRect.IsEmpty ()) // Rectangle is empty. Result is equal to region
799
const nsRectFast& aRectFast = NS_STATIC_CAST (const nsRectFast&, aRect);
801
if (!aRectFast.Intersects (aRegion.mBoundRect)) // Rectangle does not intersect region
804
InsertInPlace (new RgnRect (aRectFast), PR_TRUE);
807
// Region is simple rectangle and it fully overlays rectangle
808
if (aRegion.mRectCount == 1 && aRegion.mBoundRect.Contains (aRectFast))
811
if (aRectFast.Contains (aRegion.mBoundRect)) // Rectangle fully overlays region
815
aRegion.SubRect (aRectFast, *this); // Exclude from region parts that overlap the rectangle
816
InsertInPlace (new RgnRect (aRectFast));
826
nsRegion& nsRegion::Xor (const nsRegion& aRgn1, const nsRegion& aRgn2)
828
if (&aRgn1 == &aRgn2) // Xor with self
831
if (aRgn1.mRectCount == 0) // Region empty. Result is equal to other region
834
if (aRgn2.mRectCount == 0) // Region empty. Result is equal to other region
838
if (!aRgn1.mBoundRect.Intersects (aRgn2.mBoundRect)) // Regions do not intersect
839
Merge (aRgn1, aRgn2);
842
// Region is simple rectangle and it fully overlays other region
843
if (aRgn1.mRectCount == 1 && aRgn1.mBoundRect.Contains (aRgn2.mBoundRect))
845
aRgn1.SubRegion (aRgn2, *this);
848
// Region is simple rectangle and it fully overlays other region
849
if (aRgn2.mRectCount == 1 && aRgn2.mBoundRect.Contains (aRgn1.mBoundRect))
851
aRgn2.SubRegion (aRgn1, *this);
856
aRgn1.SubRegion (aRgn2, TmpRegion);
857
aRgn2.SubRegion (aRgn1, *this);
858
TmpRegion.MoveInto (*this);
868
nsRegion& nsRegion::Xor (const nsRegion& aRegion, const nsRect& aRect)
870
if (aRegion.mRectCount == 0) // Region empty. Result is equal to rectangle
873
if (aRect.IsEmpty ()) // Rectangle is empty. Result is equal to region
877
const nsRectFast& aRectFast = NS_STATIC_CAST (const nsRectFast&, aRect);
879
if (!aRectFast.Intersects (aRegion.mBoundRect)) // Rectangle does not intersect region
882
InsertInPlace (new RgnRect (aRectFast), PR_TRUE);
885
// Region is simple rectangle and it fully overlays rectangle
886
if (aRegion.mRectCount == 1 && aRegion.mBoundRect.Contains (aRectFast))
888
aRegion.SubRect (aRectFast, *this);
891
if (aRectFast.Contains (aRegion.mBoundRect)) // Rectangle fully overlays region
894
TmpRegion.Copy (aRectFast);
895
TmpRegion.SubRegion (aRegion, *this);
900
TmpRegion.Copy (aRectFast);
901
TmpRegion.SubRegion (aRegion, TmpRegion);
902
aRegion.SubRect (aRectFast, *this);
903
TmpRegion.MoveInto (*this);
913
nsRegion& nsRegion::Sub (const nsRegion& aRgn1, const nsRegion& aRgn2)
915
if (&aRgn1 == &aRgn2) // Sub from self
918
if (aRgn1.mRectCount == 0) // If source is empty then result is empty, too
921
if (aRgn2.mRectCount == 0) // Nothing to subtract
925
if (!aRgn1.mBoundRect.Intersects (aRgn2.mBoundRect)) // Regions do not intersect
929
aRgn1.SubRegion (aRgn2, *this);
938
nsRegion& nsRegion::Sub (const nsRegion& aRegion, const nsRect& aRect)
940
if (aRegion.mRectCount == 0) // If source is empty then result is empty, too
943
if (aRect.IsEmpty ()) // Nothing to subtract
947
const nsRectFast& aRectFast = NS_STATIC_CAST (const nsRectFast&, aRect);
949
if (!aRectFast.Intersects (aRegion.mBoundRect)) // Rectangle does not intersect region
953
if (aRectFast.Contains (aRegion.mBoundRect)) // Rectangle fully overlays region
957
aRegion.SubRect (aRectFast, *this);
967
// Subtract region from current region.
968
// Both regions are non-empty and they intersect each other.
969
// Result could be empty region if aRgn2 is rectangle that fully overlays aRgn1.
970
// Optimize () is not called on exit (bound rectangle is not updated).
972
void nsRegion::SubRegion (const nsRegion& aRegion, nsRegion& aResult) const
974
if (aRegion.mRectCount == 1) // Subtract simple rectangle
976
if (aRegion.mBoundRect.Contains (mBoundRect))
979
SubRect (*aRegion.mRectListHead.next, aResult);
982
nsRegion TmpRegion, CompletedRegion;
983
const nsRegion* pSubRgn = &aRegion;
985
if (&aResult == &aRegion) // Copy region if it is both source and result
987
TmpRegion.Copy (aRegion);
988
pSubRgn = &TmpRegion;
991
const RgnRect* pSubRect = pSubRgn->mRectListHead.next;
993
SubRect (*pSubRect, aResult, CompletedRegion);
994
pSubRect = pSubRect->next;
996
while (pSubRect != &pSubRgn->mRectListHead)
998
aResult.SubRect (*pSubRect, aResult, CompletedRegion);
999
pSubRect = pSubRect->next;
1002
CompletedRegion.MoveInto (aResult);
1007
// Subtract rectangle from current region.
1008
// Both region and rectangle are non-empty and they intersect each other.
1009
// Result could be empty region if aRect fully overlays aRegion.
1010
// Could be called repeatedly with 'this' as input and result - bound rectangle is not known.
1011
// Optimize () is not called on exit (bound rectangle is not updated).
1013
// aCompleted is filled with rectangles which are already checked and could be safely
1014
// removed from further examination in case aRect rectangles come from ordered list.
1015
// aCompleted is not automatically emptied. aCompleted and aResult could be the same region.
1017
void nsRegion::SubRect (const nsRectFast& aRect, nsRegion& aResult, nsRegion& aCompleted) const
1020
const nsRegion* pSrcRegion = this;
1022
if (&aResult == this) // Copy region if it is both source and result
1024
TmpRegion.Copy (*this);
1025
pSrcRegion = &TmpRegion;
1028
aResult.SetToElements (0);
1030
(NS_CONST_CAST (nsRegion*, pSrcRegion))->mRectListHead.y = MAX_INT32;
1031
const RgnRect* pSrcRect = pSrcRegion->mRectListHead.next;
1033
for ( ; pSrcRect->y < aRect.YMost () ; pSrcRect = pSrcRect->next)
1037
// If bottom of current rectangle is above the top of aRect then this rectangle
1038
// could be moved to aCompleted region. Successive aRect rectangles from ordered
1039
// list do not have to check this rectangle again.
1040
if (pSrcRect->YMost () <= aRect.y)
1042
aCompleted.InsertInPlace (new RgnRect (*pSrcRect));
1046
if (!TmpRect.IntersectRect (*pSrcRect, aRect))
1047
aResult.InsertInPlace (new RgnRect (*pSrcRect));
1050
// Rectangle A. Subtract from this rectangle B
1051
const nscoord ax = pSrcRect->x;
1052
const nscoord axm = pSrcRect->XMost ();
1053
const nscoord aw = pSrcRect->width;
1054
const nscoord ay = pSrcRect->y;
1055
const nscoord aym = pSrcRect->YMost ();
1056
const nscoord ah = pSrcRect->height;
1057
// Rectangle B. Subtract this from rectangle A
1058
const nscoord bx = aRect.x;
1059
const nscoord bxm = aRect.XMost ();
1060
const nscoord by = aRect.y;
1061
const nscoord bym = aRect.YMost ();
1062
// Rectangle I. Area where rectangles A and B intersect
1063
const nscoord ix = TmpRect.x;
1064
const nscoord ixm = TmpRect.XMost ();
1065
const nscoord iy = TmpRect.y;
1066
const nscoord iym = TmpRect.YMost ();
1067
const nscoord ih = TmpRect.height;
1069
// There are 16 combinations how rectangles could intersect
1071
if (bx <= ax && by <= ay)
1073
if (bxm < axm && bym < aym) // 1.
1075
aResult.InsertInPlace (new RgnRect (ixm, ay, axm - ixm, ih));
1076
aResult.InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
1078
if (bxm >= axm && bym < aym) // 2.
1080
aResult.InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
1082
if (bxm < axm && bym >= aym) // 3.
1084
aResult.InsertInPlace (new RgnRect (ixm, ay, axm - ixm, ah));
1086
if (*pSrcRect == aRect) // 4. subset
1087
{ // Current rectangle is equal to aRect
1088
break; // No any other rectangle in region can intersect it
1091
if (bx > ax && by <= ay)
1093
if (bxm < axm && bym < aym) // 5.
1095
aResult.InsertInPlace (new RgnRect (ax, ay, ix - ax, ih));
1096
aResult.InsertInPlace (new RgnRect (ixm, ay, axm - ixm, ih));
1097
aResult.InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
1099
if (bxm >= axm && bym < aym) // 6.
1101
aResult.InsertInPlace (new RgnRect (ax, ay, ix - ax, ih));
1102
aResult.InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
1104
if (bxm < axm && bym >= aym) // 7.
1106
aResult.InsertInPlace (new RgnRect (ax, ay, ix - ax, ah));
1107
aResult.InsertInPlace (new RgnRect (ixm, ay, axm - ixm, ah));
1109
if (bxm >= axm && bym >= aym) // 8.
1111
aResult.InsertInPlace (new RgnRect (ax, ay, ix - ax, ah));
1114
if (bx <= ax && by > ay)
1116
if (bxm < axm && bym < aym) // 9.
1118
aResult.InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
1119
aResult.InsertInPlace (new RgnRect (ixm, iy, axm - ixm, ih));
1120
aResult.InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
1122
if (bxm >= axm && bym < aym) // 10.
1124
aResult.InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
1125
aResult.InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
1127
if (bxm < axm && bym >= aym) // 11.
1129
aResult.InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
1130
aResult.InsertInPlace (new RgnRect (ixm, iy, axm - ixm, ih));
1132
if (bxm >= axm && bym >= aym) // 12.
1134
aResult.InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
1137
if (bx > ax && by > ay)
1139
if (bxm < axm && bym < aym) // 13.
1141
aResult.InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
1142
aResult.InsertInPlace (new RgnRect (ax, iy, ix - ax, ih));
1143
aResult.InsertInPlace (new RgnRect (ixm, iy, axm - ixm, ih));
1144
aResult.InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
1146
// Current rectangle fully overlays aRect. No any other rectangle can intersect it.
1149
if (bxm >= axm && bym < aym) // 14.
1151
aResult.InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
1152
aResult.InsertInPlace (new RgnRect (ax, iy, ix - ax, ih));
1153
aResult.InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
1155
if (bxm < axm && bym >= aym) // 15.
1157
aResult.InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
1158
aResult.InsertInPlace (new RgnRect (ax, iy, ix - ax, ih));
1159
aResult.InsertInPlace (new RgnRect (ixm, iy, axm - ixm, ih));
1161
if (bxm >= axm && bym >= aym) // 16.
1163
aResult.InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
1164
aResult.InsertInPlace (new RgnRect (ax, iy, ix - ax, ih));
1170
// Just copy remaining rectangles in region which are below aRect and can't intersect it.
1171
// If rectangles are in temporary region then they could be moved.
1172
if (pSrcRegion == &TmpRegion)
1173
TmpRegion.MoveInto (aResult, pSrcRect);
1176
while (pSrcRect != &pSrcRegion->mRectListHead)
1178
aResult.InsertInPlace (new RgnRect (*pSrcRect));
1179
pSrcRect = pSrcRect->next;
1185
PRBool nsRegion::IsEqual (const nsRegion& aRegion) const
1187
if (mRectCount == 0)
1188
return (aRegion.mRectCount == 0) ? PR_TRUE : PR_FALSE;
1190
if (aRegion.mRectCount == 0)
1191
return (mRectCount == 0) ? PR_TRUE : PR_FALSE;
1193
if (mRectCount == 1 && aRegion.mRectCount == 1) // Both regions are simple rectangles
1194
return (*mRectListHead.next == *aRegion.mRectListHead.next);
1195
else // At least one is complex region.
1197
if (mBoundRect != aRegion.mBoundRect) // If regions are equal then bounding rectangles should match
1202
TmpRegion.Xor (*this, aRegion); // Get difference between two regions
1204
return (TmpRegion.mRectCount == 0);
1210
void nsRegion::MoveBy (PRInt32 aXOffset, PRInt32 aYOffset)
1212
if (aXOffset || aYOffset)
1214
RgnRect* pRect = mRectListHead.next;
1216
while (pRect != &mRectListHead)
1218
pRect->MoveBy (aXOffset, aYOffset);
1219
pRect = pRect->next;
1222
mBoundRect.MoveBy (aXOffset, aYOffset);