1 : /* ***** BEGIN LICENSE BLOCK *****
2 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 : *
4 : * The contents of this file are subject to the Mozilla Public License Version
5 : * 1.1 (the "License"); you may not use this file except in compliance with
6 : * the License. You may obtain a copy of the License at
7 : * http://www.mozilla.org/MPL/
8 : *
9 : * Software distributed under the License is distributed on an "AS IS" basis,
10 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 : * for the specific language governing rights and limitations under the
12 : * License.
13 : *
14 : * The Original Code is mozilla.org code.
15 : *
16 : * The Initial Developer of the Original Code is
17 : * Dainis Jonitis, <Dainis_Jonitis@swh-t.lv>.
18 : * Portions created by the Initial Developer are Copyright (C) 2001
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either of the GNU General Public License Version 2 or later (the "GPL"),
25 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 : * in which case the provisions of the GPL or the LGPL are applicable instead
27 : * of those above. If you wish to allow use of your version of this file only
28 : * under the terms of either the GPL or the LGPL, and not to allow others to
29 : * use your version of this file under the terms of the MPL, indicate your
30 : * decision by deleting the provisions above and replace them with the notice
31 : * and other provisions required by the GPL or the LGPL. If you do not delete
32 : * the provisions above, a recipient may use your version of this file under
33 : * the terms of any one of the MPL, the GPL or the LGPL.
34 : *
35 : * ***** END LICENSE BLOCK ***** */
36 :
37 : #include "nsRegion.h"
38 : #include "nsISupportsImpl.h"
39 : #include "nsTArray.h"
40 :
41 : /*
42 : * The SENTINEL values below guaranties that a < or >
43 : * comparison with it will be false for all values of the
44 : * underlying nscoord type. E.g. this is always false:
45 : * aCoord > NS_COORD_GREATER_SENTINEL
46 : * Setting the mRectListHead dummy rectangle to these values
47 : * allows us to loop without checking for the list end.
48 : */
49 : #ifdef NS_COORD_IS_FLOAT
50 : #define NS_COORD_LESS_SENTINEL nscoord_MIN
51 : #define NS_COORD_GREATER_SENTINEL nscoord_MAX
52 : #else
53 : #define NS_COORD_LESS_SENTINEL PR_INT32_MIN
54 : #define NS_COORD_GREATER_SENTINEL PR_INT32_MAX
55 : #endif
56 :
57 : // Fast inline analogues of nsRect methods for nsRegion::nsRectFast.
58 : // Check for emptiness is not required - it is guaranteed by caller.
59 :
60 0 : inline bool nsRegion::nsRectFast::Contains (const nsRect& aRect) const
61 : {
62 : return (bool) ((aRect.x >= x) && (aRect.y >= y) &&
63 0 : (aRect.XMost () <= XMost ()) && (aRect.YMost () <= YMost ()));
64 : }
65 :
66 1 : inline bool nsRegion::nsRectFast::Intersects (const nsRect& aRect) const
67 : {
68 1 : return (bool) ((x < aRect.XMost ()) && (y < aRect.YMost ()) &&
69 2 : (aRect.x < XMost ()) && (aRect.y < YMost ()));
70 : }
71 :
72 0 : inline bool nsRegion::nsRectFast::IntersectRect (const nsRect& aRect1, const nsRect& aRect2)
73 : {
74 0 : const nscoord xmost = NS_MIN (aRect1.XMost (), aRect2.XMost ());
75 0 : x = NS_MAX (aRect1.x, aRect2.x);
76 0 : width = xmost - x;
77 0 : if (width <= 0) return false;
78 :
79 0 : const nscoord ymost = NS_MIN (aRect1.YMost (), aRect2.YMost ());
80 0 : y = NS_MAX (aRect1.y, aRect2.y);
81 0 : height = ymost - y;
82 0 : if (height <= 0) return false;
83 :
84 0 : return true;
85 : }
86 :
87 1 : inline void nsRegion::nsRectFast::UnionRect (const nsRect& aRect1, const nsRect& aRect2)
88 : {
89 1 : const nscoord xmost = NS_MAX (aRect1.XMost (), aRect2.XMost ());
90 1 : const nscoord ymost = NS_MAX (aRect1.YMost (), aRect2.YMost ());
91 1 : x = NS_MIN(aRect1.x, aRect2.x);
92 1 : y = NS_MIN(aRect1.y, aRect2.y);
93 1 : width = xmost - x;
94 1 : height = ymost - y;
95 1 : }
96 :
97 :
98 :
99 : // Custom memory allocator for nsRegion::RgnRect structures.
100 : // Entries are allocated from global memory pool.
101 : // Memory pool can grow in size, but it can't shrink.
102 :
103 : #define INIT_MEM_CHUNK_ENTRIES 100
104 : #define INCR_MEM_CHUNK_ENTRIES 100
105 :
106 : class RgnRectMemoryAllocator
107 : {
108 : nsRegion::RgnRect* mFreeListHead;
109 : PRUint32 mFreeEntries;
110 : void* mChunkListHead;
111 : #if defined (DEBUG)
112 : NS_DECL_OWNINGTHREAD
113 :
114 1 : void InitLock () { NS_ASSERT_OWNINGTHREAD (RgnRectMemoryAllocator); }
115 : void DestroyLock () { NS_ASSERT_OWNINGTHREAD (RgnRectMemoryAllocator); }
116 4 : void Lock () { NS_ASSERT_OWNINGTHREAD (RgnRectMemoryAllocator); }
117 4 : void Unlock () { NS_ASSERT_OWNINGTHREAD (RgnRectMemoryAllocator); }
118 : #else
119 : void InitLock () { }
120 : void DestroyLock () { }
121 : void Lock () { }
122 : void Unlock () { }
123 : #endif
124 :
125 1 : void* AllocChunk (PRUint32 aEntries, void* aNextChunk, nsRegion::RgnRect* aTailDest)
126 : {
127 2 : PRUint8* pBuf = new PRUint8 [aEntries * sizeof (nsRegion::RgnRect) + sizeof (void*)];
128 1 : *reinterpret_cast<void**>(pBuf) = aNextChunk;
129 1 : nsRegion::RgnRect* pRect = reinterpret_cast<nsRegion::RgnRect*>(pBuf + sizeof (void*));
130 :
131 100 : for (PRUint32 cnt = 0 ; cnt < aEntries - 1 ; cnt++)
132 99 : pRect [cnt].next = &pRect [cnt + 1];
133 :
134 1 : pRect [aEntries - 1].next = aTailDest;
135 :
136 1 : return pBuf;
137 : }
138 :
139 2 : void FreeChunk (void* aChunk) { delete [] (PRUint8 *) aChunk; }
140 1 : void* NextChunk (void* aThisChunk) const { return *static_cast<void**>(aThisChunk); }
141 :
142 1 : nsRegion::RgnRect* ChunkHead (void* aThisChunk) const
143 1 : { return reinterpret_cast<nsRegion::RgnRect*>(static_cast<PRUint8*>(aThisChunk) + sizeof (void*)); }
144 :
145 : public:
146 : RgnRectMemoryAllocator (PRUint32 aNumOfEntries);
147 : ~RgnRectMemoryAllocator ();
148 :
149 : nsRegion::RgnRect* Alloc ();
150 : void Free (nsRegion::RgnRect* aRect);
151 : };
152 :
153 :
154 1 : RgnRectMemoryAllocator::RgnRectMemoryAllocator (PRUint32 aNumOfEntries)
155 : {
156 1 : InitLock ();
157 1 : mChunkListHead = AllocChunk (aNumOfEntries, nsnull, nsnull);
158 1 : mFreeEntries = aNumOfEntries;
159 1 : mFreeListHead = ChunkHead (mChunkListHead);
160 1 : }
161 :
162 1 : RgnRectMemoryAllocator::~RgnRectMemoryAllocator ()
163 : {
164 3 : while (mChunkListHead)
165 : {
166 1 : void* tmp = mChunkListHead;
167 1 : mChunkListHead = NextChunk (mChunkListHead);
168 1 : FreeChunk (tmp);
169 : }
170 :
171 : #if 0
172 : /*
173 : * As a static object this class outlives any library which would implement
174 : * locking. So we intentionally leak the 'lock'.
175 : *
176 : * Currently RgnRectMemoryAllocator is only used from the primary thread,
177 : * so we aren't using a lock which means that there is no lock to leak.
178 : * If we ever switch to multiple GUI threads (e.g. one per window),
179 : * we'd probably use one allocator per window-thread to avoid the
180 : * locking overhead and just require consumers not to pass regions
181 : * across threads/windows, which would be a reasonable restriction
182 : * because they wouldn't be useful outside their window.
183 : */
184 : DestroyLock ();
185 : #endif
186 1 : }
187 :
188 2 : nsRegion::RgnRect* RgnRectMemoryAllocator::Alloc ()
189 : {
190 2 : Lock ();
191 :
192 2 : if (mFreeEntries == 0)
193 : {
194 0 : mChunkListHead = AllocChunk (INCR_MEM_CHUNK_ENTRIES, mChunkListHead, mFreeListHead);
195 0 : mFreeEntries = INCR_MEM_CHUNK_ENTRIES;
196 0 : mFreeListHead = ChunkHead (mChunkListHead);
197 : }
198 :
199 2 : nsRegion::RgnRect* tmp = mFreeListHead;
200 2 : mFreeListHead = mFreeListHead->next;
201 2 : mFreeEntries--;
202 2 : Unlock ();
203 :
204 2 : return tmp;
205 : }
206 :
207 2 : void RgnRectMemoryAllocator::Free (nsRegion::RgnRect* aRect)
208 : {
209 2 : Lock ();
210 2 : mFreeEntries++;
211 2 : aRect->next = mFreeListHead;
212 2 : mFreeListHead = aRect;
213 2 : Unlock ();
214 2 : }
215 :
216 :
217 : // Global pool for nsRegion::RgnRect allocation
218 : static PRUintn gRectPoolTlsIndex;
219 :
220 1 : void RgnRectMemoryAllocatorDTOR(void *priv)
221 : {
222 : RgnRectMemoryAllocator* allocator = (static_cast<RgnRectMemoryAllocator*>(
223 1 : PR_GetThreadPrivate(gRectPoolTlsIndex)));
224 1 : delete allocator;
225 1 : }
226 :
227 1404 : nsresult nsRegion::InitStatic()
228 : {
229 1404 : return PR_NewThreadPrivateIndex(&gRectPoolTlsIndex, RgnRectMemoryAllocatorDTOR);
230 : }
231 :
232 1403 : void nsRegion::ShutdownStatic()
233 : {
234 : RgnRectMemoryAllocator* allocator = (static_cast<RgnRectMemoryAllocator*>(
235 1403 : PR_GetThreadPrivate(gRectPoolTlsIndex)));
236 1403 : if (!allocator)
237 1402 : return;
238 :
239 1 : delete allocator;
240 :
241 1 : PR_SetThreadPrivate(gRectPoolTlsIndex, nsnull);
242 : }
243 :
244 2 : void* nsRegion::RgnRect::operator new (size_t) CPP_THROW_NEW
245 : {
246 : RgnRectMemoryAllocator* allocator = (static_cast<RgnRectMemoryAllocator*>(
247 2 : PR_GetThreadPrivate(gRectPoolTlsIndex)));
248 2 : if (!allocator) {
249 1 : allocator = new RgnRectMemoryAllocator(INIT_MEM_CHUNK_ENTRIES);
250 1 : PR_SetThreadPrivate(gRectPoolTlsIndex, allocator);
251 : }
252 2 : return allocator->Alloc ();
253 : }
254 :
255 2 : void nsRegion::RgnRect::operator delete (void* aRect, size_t)
256 : {
257 : RgnRectMemoryAllocator* allocator = (static_cast<RgnRectMemoryAllocator*>(
258 2 : PR_GetThreadPrivate(gRectPoolTlsIndex)));
259 2 : if (!allocator) {
260 0 : NS_ERROR("Invalid nsRegion::RgnRect delete");
261 0 : return;
262 : }
263 2 : allocator->Free (static_cast<RgnRect*>(aRect));
264 : }
265 :
266 :
267 :
268 1 : void nsRegion::Init()
269 : {
270 1 : mRectListHead.prev = mRectListHead.next = &mRectListHead;
271 1 : mCurRect = &mRectListHead;
272 1 : mRectCount = 0;
273 1 : mBoundRect.SetRect (0, 0, 0, 0);
274 1 : }
275 :
276 0 : inline void nsRegion::InsertBefore (RgnRect* aNewRect, RgnRect* aRelativeRect)
277 : {
278 0 : aNewRect->prev = aRelativeRect->prev;
279 0 : aNewRect->next = aRelativeRect;
280 0 : aRelativeRect->prev->next = aNewRect;
281 0 : aRelativeRect->prev = aNewRect;
282 0 : mCurRect = aNewRect;
283 0 : mRectCount++;
284 0 : }
285 :
286 1 : inline void nsRegion::InsertAfter (RgnRect* aNewRect, RgnRect* aRelativeRect)
287 : {
288 1 : aNewRect->prev = aRelativeRect;
289 1 : aNewRect->next = aRelativeRect->next;
290 1 : aRelativeRect->next->prev = aNewRect;
291 1 : aRelativeRect->next = aNewRect;
292 1 : mCurRect = aNewRect;
293 1 : mRectCount++;
294 1 : }
295 :
296 :
297 : // Adjust the number of rectangles in region.
298 : // Content of rectangles should be changed by caller.
299 :
300 2 : void nsRegion::SetToElements (PRUint32 aCount)
301 : {
302 2 : if (mRectCount < aCount) // Add missing rectangles
303 : {
304 1 : PRUint32 InsertCount = aCount - mRectCount;
305 1 : mRectCount = aCount;
306 1 : RgnRect* pPrev = &mRectListHead;
307 1 : RgnRect* pNext = mRectListHead.next;
308 :
309 3 : while (InsertCount--)
310 : {
311 1 : mCurRect = new RgnRect;
312 1 : mCurRect->prev = pPrev;
313 1 : pPrev->next = mCurRect;
314 1 : pPrev = mCurRect;
315 : }
316 :
317 1 : pPrev->next = pNext;
318 1 : pNext->prev = pPrev;
319 : } else
320 1 : if (mRectCount > aCount) // Remove unnecessary rectangles
321 : {
322 1 : PRUint32 RemoveCount = mRectCount - aCount;
323 1 : mRectCount = aCount;
324 1 : mCurRect = mRectListHead.next;
325 :
326 4 : while (RemoveCount--)
327 : {
328 2 : RgnRect* tmp = mCurRect;
329 2 : mCurRect = mCurRect->next;
330 2 : delete tmp;
331 : }
332 :
333 1 : mRectListHead.next = mCurRect;
334 1 : mCurRect->prev = &mRectListHead;
335 : }
336 2 : }
337 :
338 :
339 : // Save the entire chain of linked elements in 'prev' field of the RgnRect structure.
340 : // After that forward-only iterations using 'next' field could still be used.
341 : // Some elements from forward-only chain could be temporarily removed to optimize inner loops.
342 : // The original double linked state could be restored by call to RestoreLinkChain ().
343 : // Both functions despite size can be inline because they are called only from one function.
344 :
345 0 : inline void nsRegion::SaveLinkChain ()
346 : {
347 0 : RgnRect* pRect = &mRectListHead;
348 :
349 0 : do
350 : {
351 0 : pRect->prev = pRect->next;
352 0 : pRect = pRect->next;
353 : } while (pRect != &mRectListHead);
354 0 : }
355 :
356 :
357 0 : inline void nsRegion::RestoreLinkChain ()
358 : {
359 0 : RgnRect* pPrev = &mRectListHead;
360 0 : RgnRect* pRect = mRectListHead.next = mRectListHead.prev;
361 :
362 0 : while (pRect != &mRectListHead)
363 : {
364 0 : pRect->next = pRect->prev;
365 0 : pRect->prev = pPrev;
366 0 : pPrev = pRect;
367 0 : pRect = pRect->next;
368 : }
369 :
370 0 : mRectListHead.prev = pPrev;
371 0 : }
372 :
373 :
374 : // Insert node in right place of sorted list
375 : // If necessary then bounding rectangle could be updated and rectangle combined
376 : // with neighbour rectangles. This is usually done in Optimize ()
377 :
378 1 : void nsRegion::InsertInPlace (RgnRect* aRect, bool aOptimizeOnFly)
379 : {
380 1 : if (mRectCount == 0)
381 0 : InsertAfter (aRect, &mRectListHead);
382 : else
383 : {
384 1 : if (aRect->y > mCurRect->y)
385 : {
386 1 : mRectListHead.y = NS_COORD_GREATER_SENTINEL;
387 2 : while (aRect->y > mCurRect->next->y)
388 0 : mCurRect = mCurRect->next;
389 :
390 1 : mRectListHead.x = NS_COORD_GREATER_SENTINEL;
391 2 : while (aRect->y == mCurRect->next->y && aRect->x > mCurRect->next->x)
392 0 : mCurRect = mCurRect->next;
393 :
394 1 : InsertAfter (aRect, mCurRect);
395 : } else
396 0 : if (aRect->y < mCurRect->y)
397 : {
398 0 : mRectListHead.y = NS_COORD_LESS_SENTINEL;
399 0 : while (aRect->y < mCurRect->prev->y)
400 0 : mCurRect = mCurRect->prev;
401 :
402 0 : mRectListHead.x = NS_COORD_LESS_SENTINEL;
403 0 : while (aRect->y == mCurRect->prev->y && aRect->x < mCurRect->prev->x)
404 0 : mCurRect = mCurRect->prev;
405 :
406 0 : InsertBefore (aRect, mCurRect);
407 : } else
408 : {
409 0 : if (aRect->x > mCurRect->x)
410 : {
411 0 : mRectListHead.x = NS_COORD_GREATER_SENTINEL;
412 0 : while (aRect->y == mCurRect->next->y && aRect->x > mCurRect->next->x)
413 0 : mCurRect = mCurRect->next;
414 :
415 0 : InsertAfter (aRect, mCurRect);
416 : } else
417 : {
418 0 : mRectListHead.x = NS_COORD_LESS_SENTINEL;
419 0 : while (aRect->y == mCurRect->prev->y && aRect->x < mCurRect->prev->x)
420 0 : mCurRect = mCurRect->prev;
421 :
422 0 : InsertBefore (aRect, mCurRect);
423 : }
424 : }
425 : }
426 :
427 :
428 1 : if (aOptimizeOnFly)
429 : {
430 1 : if (mRectCount == 1)
431 0 : mBoundRect = *mCurRect;
432 : else
433 : {
434 1 : mBoundRect.UnionRect (mBoundRect, *mCurRect);
435 :
436 : // Check if we can go left or up before starting to combine rectangles
437 1 : if ((mCurRect->y == mCurRect->prev->y && mCurRect->height == mCurRect->prev->height &&
438 0 : mCurRect->x == mCurRect->prev->XMost ()) ||
439 : (mCurRect->x == mCurRect->prev->x && mCurRect->width == mCurRect->prev->width &&
440 0 : mCurRect->y == mCurRect->prev->YMost ()) )
441 0 : mCurRect = mCurRect->prev;
442 :
443 : // Try to combine with rectangle on right side
444 2 : while (mCurRect->y == mCurRect->next->y && mCurRect->height == mCurRect->next->height &&
445 0 : mCurRect->XMost () == mCurRect->next->x)
446 : {
447 0 : mCurRect->width += mCurRect->next->width;
448 0 : delete Remove (mCurRect->next);
449 : }
450 :
451 : // Try to combine with rectangle under this one
452 2 : while (mCurRect->x == mCurRect->next->x && mCurRect->width == mCurRect->next->width &&
453 0 : mCurRect->YMost () == mCurRect->next->y)
454 : {
455 0 : mCurRect->height += mCurRect->next->height;
456 0 : delete Remove (mCurRect->next);
457 : }
458 : }
459 : }
460 1 : }
461 :
462 :
463 0 : nsRegion::RgnRect* nsRegion::Remove (RgnRect* aRect)
464 : {
465 0 : aRect->prev->next = aRect->next;
466 0 : aRect->next->prev = aRect->prev;
467 0 : mRectCount--;
468 :
469 0 : if (mCurRect == aRect)
470 0 : mCurRect = (aRect->next != &mRectListHead) ? aRect->next : aRect->prev;
471 :
472 0 : return aRect;
473 : }
474 :
475 :
476 : // Try to reduce the number of rectangles in complex region by combining with
477 : // surrounding ones on right and bottom sides of each rectangle in list.
478 : // Update bounding rectangle
479 :
480 0 : void nsRegion::Optimize ()
481 : {
482 0 : if (mRectCount == 0)
483 0 : mBoundRect.SetRect (0, 0, 0, 0);
484 : else
485 : {
486 0 : RgnRect* pRect = mRectListHead.next;
487 0 : PRInt32 xmost = mRectListHead.prev->XMost ();
488 0 : PRInt32 ymost = mRectListHead.prev->YMost ();
489 0 : mBoundRect.x = mRectListHead.next->x;
490 0 : mBoundRect.y = mRectListHead.next->y;
491 :
492 0 : while (pRect != &mRectListHead)
493 : {
494 : // Try to combine with rectangle on right side
495 0 : while (pRect->y == pRect->next->y && pRect->height == pRect->next->height &&
496 0 : pRect->XMost () == pRect->next->x)
497 : {
498 0 : pRect->width += pRect->next->width;
499 0 : delete Remove (pRect->next);
500 : }
501 :
502 : // Try to combine with rectangle under this one
503 0 : while (pRect->x == pRect->next->x && pRect->width == pRect->next->width &&
504 0 : pRect->YMost () == pRect->next->y)
505 : {
506 0 : pRect->height += pRect->next->height;
507 0 : delete Remove (pRect->next);
508 : }
509 :
510 : // Determine bound rectangle. Use fact that rectangles are sorted.
511 0 : if (pRect->x < mBoundRect.x) mBoundRect.x = pRect->x;
512 0 : if (pRect->XMost () > xmost) xmost = pRect->XMost ();
513 0 : if (pRect->YMost () > ymost) ymost = pRect->YMost ();
514 :
515 0 : pRect = pRect->next;
516 : }
517 :
518 0 : mBoundRect.width = xmost - mBoundRect.x;
519 0 : mBoundRect.height = ymost - mBoundRect.y;
520 : }
521 0 : }
522 :
523 :
524 : // Move rectangles starting from 'aStartRect' till end of the list to the destionation region.
525 : // Important for temporary objects - instead of copying rectangles with Merge () and then
526 : // emptying region in destructor they could be moved to destination region in one step.
527 :
528 0 : void nsRegion::MoveInto (nsRegion& aDestRegion, const RgnRect* aStartRect)
529 : {
530 0 : RgnRect* pRect = const_cast<RgnRect*>(aStartRect);
531 0 : RgnRect* pPrev = pRect->prev;
532 :
533 0 : while (pRect != &mRectListHead)
534 : {
535 0 : RgnRect* next = pRect->next;
536 0 : aDestRegion.InsertInPlace (pRect);
537 :
538 0 : mRectCount--;
539 0 : pRect = next;
540 : }
541 :
542 0 : pPrev->next = &mRectListHead;
543 0 : mRectListHead.prev = pPrev;
544 0 : mCurRect = mRectListHead.next;
545 0 : }
546 :
547 :
548 : // Merge two non-overlapping regions into one.
549 : // Automatically optimize region by calling Optimize ()
550 :
551 0 : void nsRegion::Merge (const nsRegion& aRgn1, const nsRegion& aRgn2)
552 : {
553 0 : if (aRgn1.mRectCount == 0) // Region empty. Result is equal to other region
554 0 : Copy (aRgn2);
555 : else
556 0 : if (aRgn2.mRectCount == 0) // Region empty. Result is equal to other region
557 0 : Copy (aRgn1);
558 0 : if (aRgn1.mRectCount == 1) // Region is single rectangle. Optimize on fly
559 : {
560 0 : RgnRect* TmpRect = new RgnRect (*aRgn1.mRectListHead.next);
561 0 : Copy (aRgn2);
562 0 : InsertInPlace (TmpRect, true);
563 : } else
564 0 : if (aRgn2.mRectCount == 1) // Region is single rectangle. Optimize on fly
565 : {
566 0 : RgnRect* TmpRect = new RgnRect (*aRgn2.mRectListHead.next);
567 0 : Copy (aRgn1);
568 0 : InsertInPlace (TmpRect, true);
569 : } else
570 : {
571 : const nsRegion* pCopyRegion, *pInsertRegion;
572 :
573 : // Determine which region contains more rectangles. Copy the larger one
574 0 : if (aRgn1.mRectCount >= aRgn2.mRectCount)
575 : {
576 0 : pCopyRegion = &aRgn1;
577 0 : pInsertRegion = &aRgn2;
578 : } else
579 : {
580 0 : pCopyRegion = &aRgn2;
581 0 : pInsertRegion = &aRgn1;
582 : }
583 :
584 0 : if (pInsertRegion == this) // Do merge in-place
585 0 : pInsertRegion = pCopyRegion;
586 : else
587 0 : Copy (*pCopyRegion);
588 :
589 0 : const RgnRect* pSrcRect = pInsertRegion->mRectListHead.next;
590 :
591 0 : while (pSrcRect != &pInsertRegion->mRectListHead)
592 : {
593 0 : InsertInPlace (new RgnRect (*pSrcRect));
594 :
595 0 : pSrcRect = pSrcRect->next;
596 : }
597 :
598 0 : Optimize ();
599 : }
600 0 : }
601 :
602 :
603 1 : nsRegion& nsRegion::Copy (const nsRegion& aRegion)
604 : {
605 1 : if (&aRegion == this)
606 1 : return *this;
607 :
608 0 : if (aRegion.mRectCount == 0)
609 0 : SetEmpty ();
610 : else
611 : {
612 0 : SetToElements (aRegion.mRectCount);
613 :
614 0 : const RgnRect* pSrc = aRegion.mRectListHead.next;
615 0 : RgnRect* pDest = mRectListHead.next;
616 :
617 0 : while (pSrc != &aRegion.mRectListHead)
618 : {
619 0 : *pDest = *pSrc;
620 :
621 0 : pSrc = pSrc->next;
622 0 : pDest = pDest->next;
623 : }
624 :
625 0 : mCurRect = mRectListHead.next;
626 0 : mBoundRect = aRegion.mBoundRect;
627 : }
628 :
629 0 : return *this;
630 : }
631 :
632 :
633 1 : nsRegion& nsRegion::Copy (const nsRect& aRect)
634 : {
635 1 : if (aRect.IsEmpty ())
636 0 : SetEmpty ();
637 : else
638 : {
639 1 : SetToElements (1);
640 1 : *mRectListHead.next = static_cast<const RgnRect&>(aRect);
641 1 : mBoundRect = static_cast<const nsRectFast&>(aRect);
642 : }
643 :
644 1 : return *this;
645 : }
646 :
647 :
648 0 : nsRegion& nsRegion::And (const nsRegion& aRgn1, const nsRegion& aRgn2)
649 : {
650 0 : if (&aRgn1 == &aRgn2) // And with self
651 0 : Copy (aRgn1);
652 : else
653 0 : if (aRgn1.mRectCount == 0 || aRgn2.mRectCount == 0) // If either region is empty then result is empty
654 0 : SetEmpty ();
655 : else
656 : {
657 0 : nsRectFast TmpRect;
658 :
659 0 : if (aRgn1.mRectCount == 1 && aRgn2.mRectCount == 1) // Intersect rectangle with rectangle
660 : {
661 0 : TmpRect.IntersectRect (*aRgn1.mRectListHead.next, *aRgn2.mRectListHead.next);
662 0 : Copy (TmpRect);
663 : } else
664 : {
665 0 : if (!aRgn1.mBoundRect.Intersects (aRgn2.mBoundRect)) // Regions do not intersect
666 0 : SetEmpty ();
667 : else
668 : {
669 : // Region is simple rectangle and it fully overlays other region
670 0 : if (aRgn1.mRectCount == 1 && aRgn1.mBoundRect.Contains (aRgn2.mBoundRect))
671 0 : Copy (aRgn2);
672 : else
673 : // Region is simple rectangle and it fully overlays other region
674 0 : if (aRgn2.mRectCount == 1 && aRgn2.mBoundRect.Contains (aRgn1.mBoundRect))
675 0 : Copy (aRgn1);
676 : else
677 : {
678 0 : nsRegion TmpRegion;
679 0 : nsRegion* pSrcRgn1 = const_cast<nsRegion*>(&aRgn1);
680 0 : nsRegion* pSrcRgn2 = const_cast<nsRegion*>(&aRgn2);
681 :
682 0 : if (&aRgn1 == this) // Copy region if it is both source and result
683 : {
684 0 : TmpRegion.Copy (aRgn1);
685 0 : pSrcRgn1 = &TmpRegion;
686 : }
687 :
688 0 : if (&aRgn2 == this) // Copy region if it is both source and result
689 : {
690 0 : TmpRegion.Copy (aRgn2);
691 0 : pSrcRgn2 = &TmpRegion;
692 : }
693 :
694 : // For outer loop prefer region for which at least one rectangle is below other's bound rectangle
695 0 : if (pSrcRgn2->mRectListHead.prev->y >= pSrcRgn1->mBoundRect.YMost ())
696 : {
697 0 : nsRegion* Tmp = pSrcRgn1;
698 0 : pSrcRgn1 = pSrcRgn2;
699 0 : pSrcRgn2 = Tmp;
700 : }
701 :
702 :
703 0 : SetToElements (0);
704 0 : pSrcRgn2->SaveLinkChain ();
705 :
706 0 : pSrcRgn1->mRectListHead.y = NS_COORD_GREATER_SENTINEL;
707 0 : pSrcRgn2->mRectListHead.y = NS_COORD_GREATER_SENTINEL;
708 :
709 0 : for (RgnRect* pSrcRect1 = pSrcRgn1->mRectListHead.next ;
710 0 : pSrcRect1->y < pSrcRgn2->mBoundRect.YMost () ; pSrcRect1 = pSrcRect1->next)
711 : {
712 0 : if (pSrcRect1->Intersects (pSrcRgn2->mBoundRect)) // Rectangle intersects region. Process each rectangle
713 : {
714 0 : RgnRect* pPrev2 = &pSrcRgn2->mRectListHead;
715 :
716 0 : for (RgnRect* pSrcRect2 = pSrcRgn2->mRectListHead.next ;
717 0 : pSrcRect2->y < pSrcRect1->YMost () ; pSrcRect2 = pSrcRect2->next)
718 : {
719 0 : if (pSrcRect2->YMost () <= pSrcRect1->y) // Rect2's bottom is above the top of Rect1.
720 : { // No successive rectangles in Rgn1 can intersect it.
721 0 : pPrev2->next = pSrcRect2->next; // Remove Rect2 from Rgn2's checklist
722 0 : continue;
723 : }
724 :
725 0 : if (pSrcRect1->Contains (*pSrcRect2)) // Rect1 fully overlays Rect2.
726 : { // No any other rectangle in Rgn1 can intersect it.
727 0 : pPrev2->next = pSrcRect2->next; // Remove Rect2 from Rgn2's checklist
728 0 : InsertInPlace (new RgnRect (*pSrcRect2));
729 0 : continue;
730 : }
731 :
732 :
733 0 : if (TmpRect.IntersectRect (*pSrcRect1, *pSrcRect2))
734 0 : InsertInPlace (new RgnRect (TmpRect));
735 :
736 0 : pPrev2 = pSrcRect2;
737 : }
738 : }
739 : }
740 :
741 0 : pSrcRgn2->RestoreLinkChain ();
742 0 : Optimize ();
743 : }
744 : }
745 : }
746 : }
747 :
748 0 : return *this;
749 : }
750 :
751 :
752 0 : nsRegion& nsRegion::And (const nsRegion& aRegion, const nsRect& aRect)
753 : {
754 : // If either region or rectangle is empty then result is empty
755 0 : if (aRegion.mRectCount == 0 || aRect.IsEmpty ())
756 0 : SetEmpty ();
757 : else // Intersect region with rectangle
758 : {
759 0 : const nsRectFast& aRectFast = static_cast<const nsRectFast&>(aRect);
760 0 : nsRectFast TmpRect;
761 :
762 0 : if (aRegion.mRectCount == 1) // Intersect rectangle with rectangle
763 : {
764 0 : TmpRect.IntersectRect (*aRegion.mRectListHead.next, aRectFast);
765 0 : Copy (TmpRect);
766 : } else // Intersect complex region with rectangle
767 : {
768 0 : if (!aRectFast.Intersects (aRegion.mBoundRect)) // Rectangle does not intersect region
769 0 : SetEmpty ();
770 : else
771 : {
772 0 : if (aRectFast.Contains (aRegion.mBoundRect)) // Rectangle fully overlays region
773 0 : Copy (aRegion);
774 : else
775 : {
776 0 : nsRegion TmpRegion;
777 0 : nsRegion* pSrcRegion = const_cast<nsRegion*>(&aRegion);
778 :
779 0 : if (&aRegion == this) // Copy region if it is both source and result
780 : {
781 0 : TmpRegion.Copy (aRegion);
782 0 : pSrcRegion = &TmpRegion;
783 : }
784 :
785 0 : SetToElements (0);
786 0 : pSrcRegion->mRectListHead.y = NS_COORD_GREATER_SENTINEL;
787 :
788 0 : for (const RgnRect* pSrcRect = pSrcRegion->mRectListHead.next ;
789 0 : pSrcRect->y < aRectFast.YMost () ; pSrcRect = pSrcRect->next)
790 : {
791 0 : if (TmpRect.IntersectRect (*pSrcRect, aRectFast))
792 0 : InsertInPlace (new RgnRect (TmpRect));
793 : }
794 :
795 0 : Optimize ();
796 : }
797 : }
798 : }
799 : }
800 :
801 0 : return *this;
802 : }
803 :
804 :
805 0 : nsRegion& nsRegion::Or (const nsRegion& aRgn1, const nsRegion& aRgn2)
806 : {
807 0 : if (&aRgn1 == &aRgn2) // Or with self
808 0 : Copy (aRgn1);
809 : else
810 0 : if (aRgn1.mRectCount == 0) // Region empty. Result is equal to other region
811 0 : Copy (aRgn2);
812 : else
813 0 : if (aRgn2.mRectCount == 0) // Region empty. Result is equal to other region
814 0 : Copy (aRgn1);
815 : else
816 : {
817 0 : if (!aRgn1.mBoundRect.Intersects (aRgn2.mBoundRect)) // Regions do not intersect
818 0 : Merge (aRgn1, aRgn2);
819 : else
820 : {
821 : // Region is simple rectangle and it fully overlays other region
822 0 : if (aRgn1.mRectCount == 1 && aRgn1.mBoundRect.Contains (aRgn2.mBoundRect))
823 0 : Copy (aRgn1);
824 : else
825 : // Region is simple rectangle and it fully overlays other region
826 0 : if (aRgn2.mRectCount == 1 && aRgn2.mBoundRect.Contains (aRgn1.mBoundRect))
827 0 : Copy (aRgn2);
828 : else
829 : {
830 0 : nsRegion TmpRegion;
831 0 : aRgn1.SubRegion (aRgn2, TmpRegion); // Get only parts of region which not overlap the other region
832 0 : Copy (aRgn2);
833 0 : TmpRegion.MoveInto (*this);
834 0 : Optimize ();
835 : }
836 : }
837 : }
838 :
839 0 : return *this;
840 : }
841 :
842 :
843 2 : nsRegion& nsRegion::Or (const nsRegion& aRegion, const nsRect& aRect)
844 : {
845 2 : if (aRegion.mRectCount == 0) // Region empty. Result is equal to rectangle
846 1 : Copy (aRect);
847 : else
848 1 : if (aRect.IsEmpty ()) // Rectangle is empty. Result is equal to region
849 0 : Copy (aRegion);
850 : else
851 : {
852 1 : const nsRectFast& aRectFast = static_cast<const nsRectFast&>(aRect);
853 :
854 1 : if (!aRectFast.Intersects (aRegion.mBoundRect)) // Rectangle does not intersect region
855 : {
856 1 : Copy (aRegion);
857 1 : InsertInPlace (new RgnRect (aRectFast), true);
858 : } else
859 : {
860 : // Region is simple rectangle and it fully overlays rectangle
861 0 : if (aRegion.mRectCount == 1 && aRegion.mBoundRect.Contains (aRectFast))
862 0 : Copy (aRegion);
863 : else
864 0 : if (aRectFast.Contains (aRegion.mBoundRect)) // Rectangle fully overlays region
865 0 : Copy (aRectFast);
866 : else
867 : {
868 0 : aRegion.SubRect (aRectFast, *this); // Exclude from region parts that overlap the rectangle
869 0 : InsertInPlace (new RgnRect (aRectFast));
870 0 : Optimize ();
871 : }
872 : }
873 : }
874 :
875 2 : return *this;
876 : }
877 :
878 :
879 0 : nsRegion& nsRegion::Xor (const nsRegion& aRgn1, const nsRegion& aRgn2)
880 : {
881 0 : if (&aRgn1 == &aRgn2) // Xor with self
882 0 : SetEmpty ();
883 : else
884 0 : if (aRgn1.mRectCount == 0) // Region empty. Result is equal to other region
885 0 : Copy (aRgn2);
886 : else
887 0 : if (aRgn2.mRectCount == 0) // Region empty. Result is equal to other region
888 0 : Copy (aRgn1);
889 : else
890 : {
891 0 : if (!aRgn1.mBoundRect.Intersects (aRgn2.mBoundRect)) // Regions do not intersect
892 0 : Merge (aRgn1, aRgn2);
893 : else
894 : {
895 : // Region is simple rectangle and it fully overlays other region
896 0 : if (aRgn1.mRectCount == 1 && aRgn1.mBoundRect.Contains (aRgn2.mBoundRect))
897 : {
898 0 : aRgn1.SubRegion (aRgn2, *this);
899 0 : Optimize ();
900 : } else
901 : // Region is simple rectangle and it fully overlays other region
902 0 : if (aRgn2.mRectCount == 1 && aRgn2.mBoundRect.Contains (aRgn1.mBoundRect))
903 : {
904 0 : aRgn2.SubRegion (aRgn1, *this);
905 0 : Optimize ();
906 : } else
907 : {
908 0 : nsRegion TmpRegion;
909 0 : aRgn1.SubRegion (aRgn2, TmpRegion);
910 0 : aRgn2.SubRegion (aRgn1, *this);
911 0 : TmpRegion.MoveInto (*this);
912 0 : Optimize ();
913 : }
914 : }
915 : }
916 :
917 0 : return *this;
918 : }
919 :
920 :
921 0 : nsRegion& nsRegion::Xor (const nsRegion& aRegion, const nsRect& aRect)
922 : {
923 0 : if (aRegion.mRectCount == 0) // Region empty. Result is equal to rectangle
924 0 : Copy (aRect);
925 : else
926 0 : if (aRect.IsEmpty ()) // Rectangle is empty. Result is equal to region
927 0 : Copy (aRegion);
928 : else
929 : {
930 0 : const nsRectFast& aRectFast = static_cast<const nsRectFast&>(aRect);
931 :
932 0 : if (!aRectFast.Intersects (aRegion.mBoundRect)) // Rectangle does not intersect region
933 : {
934 0 : Copy (aRegion);
935 0 : InsertInPlace (new RgnRect (aRectFast), true);
936 : } else
937 : {
938 : // Region is simple rectangle and it fully overlays rectangle
939 0 : if (aRegion.mRectCount == 1 && aRegion.mBoundRect.Contains (aRectFast))
940 : {
941 0 : aRegion.SubRect (aRectFast, *this);
942 0 : Optimize ();
943 : } else
944 0 : if (aRectFast.Contains (aRegion.mBoundRect)) // Rectangle fully overlays region
945 : {
946 0 : nsRegion TmpRegion;
947 0 : TmpRegion.Copy (aRectFast);
948 0 : TmpRegion.SubRegion (aRegion, *this);
949 0 : Optimize ();
950 : } else
951 : {
952 0 : nsRegion TmpRegion;
953 0 : TmpRegion.Copy (aRectFast);
954 0 : TmpRegion.SubRegion (aRegion, TmpRegion);
955 0 : aRegion.SubRect (aRectFast, *this);
956 0 : TmpRegion.MoveInto (*this);
957 0 : Optimize ();
958 : }
959 : }
960 : }
961 :
962 0 : return *this;
963 : }
964 :
965 :
966 0 : nsRegion& nsRegion::Sub (const nsRegion& aRgn1, const nsRegion& aRgn2)
967 : {
968 0 : if (&aRgn1 == &aRgn2) // Sub from self
969 0 : SetEmpty ();
970 : else
971 0 : if (aRgn1.mRectCount == 0) // If source is empty then result is empty, too
972 0 : SetEmpty ();
973 : else
974 0 : if (aRgn2.mRectCount == 0) // Nothing to subtract
975 0 : Copy (aRgn1);
976 : else
977 : {
978 0 : if (!aRgn1.mBoundRect.Intersects (aRgn2.mBoundRect)) // Regions do not intersect
979 0 : Copy (aRgn1);
980 : else
981 : {
982 0 : aRgn1.SubRegion (aRgn2, *this);
983 0 : Optimize ();
984 : }
985 : }
986 :
987 0 : return *this;
988 : }
989 :
990 :
991 0 : nsRegion& nsRegion::Sub (const nsRegion& aRegion, const nsRect& aRect)
992 : {
993 0 : if (aRegion.mRectCount == 0) // If source is empty then result is empty, too
994 0 : SetEmpty ();
995 : else
996 0 : if (aRect.IsEmpty ()) // Nothing to subtract
997 0 : Copy (aRegion);
998 : else
999 : {
1000 0 : const nsRectFast& aRectFast = static_cast<const nsRectFast&>(aRect);
1001 :
1002 0 : if (!aRectFast.Intersects (aRegion.mBoundRect)) // Rectangle does not intersect region
1003 0 : Copy (aRegion);
1004 : else
1005 : {
1006 0 : if (aRectFast.Contains (aRegion.mBoundRect)) // Rectangle fully overlays region
1007 0 : SetEmpty ();
1008 : else
1009 : {
1010 0 : aRegion.SubRect (aRectFast, *this);
1011 0 : Optimize ();
1012 : }
1013 : }
1014 : }
1015 :
1016 0 : return *this;
1017 : }
1018 :
1019 0 : bool nsRegion::Contains (const nsRect& aRect) const
1020 : {
1021 0 : if (aRect.IsEmpty())
1022 0 : return true;
1023 0 : if (IsEmpty())
1024 0 : return false;
1025 0 : if (!IsComplex())
1026 0 : return mBoundRect.Contains (aRect);
1027 :
1028 0 : nsRegion tmpRgn;
1029 0 : tmpRgn.Sub(aRect, *this);
1030 0 : return tmpRgn.IsEmpty();
1031 : }
1032 :
1033 0 : bool nsRegion::Contains (const nsRegion& aRgn) const
1034 : {
1035 : // XXX this could be made faster
1036 0 : nsRegionRectIterator iter(aRgn);
1037 0 : while (const nsRect* r = iter.Next()) {
1038 0 : if (!Contains (*r)) {
1039 0 : return false;
1040 : }
1041 : }
1042 0 : return true;
1043 : }
1044 :
1045 0 : bool nsRegion::Intersects (const nsRect& aRect) const
1046 : {
1047 0 : if (aRect.IsEmpty() || IsEmpty())
1048 0 : return false;
1049 :
1050 0 : const RgnRect* r = mRectListHead.next;
1051 0 : while (r != &mRectListHead)
1052 : {
1053 0 : if (r->Intersects(aRect))
1054 0 : return true;
1055 0 : r = r->next;
1056 : }
1057 0 : return false;
1058 : }
1059 :
1060 : // Subtract region from current region.
1061 : // Both regions are non-empty and they intersect each other.
1062 : // Result could be empty region if aRgn2 is rectangle that fully overlays aRgn1.
1063 : // Optimize () is not called on exit (bound rectangle is not updated).
1064 :
1065 0 : void nsRegion::SubRegion (const nsRegion& aRegion, nsRegion& aResult) const
1066 : {
1067 0 : if (aRegion.mRectCount == 1) // Subtract simple rectangle
1068 : {
1069 0 : if (aRegion.mBoundRect.Contains (mBoundRect))
1070 0 : aResult.SetEmpty ();
1071 : else
1072 0 : SubRect (*aRegion.mRectListHead.next, aResult);
1073 : } else
1074 : {
1075 0 : nsRegion TmpRegion, CompletedRegion;
1076 0 : const nsRegion* pSubRgn = &aRegion;
1077 :
1078 0 : if (&aResult == &aRegion) // Copy region if it is both source and result
1079 : {
1080 0 : TmpRegion.Copy (aRegion);
1081 0 : pSubRgn = &TmpRegion;
1082 : }
1083 :
1084 0 : const RgnRect* pSubRect = pSubRgn->mRectListHead.next;
1085 :
1086 0 : SubRect (*pSubRect, aResult, CompletedRegion);
1087 0 : pSubRect = pSubRect->next;
1088 :
1089 0 : while (pSubRect != &pSubRgn->mRectListHead)
1090 : {
1091 0 : aResult.SubRect (*pSubRect, aResult, CompletedRegion);
1092 0 : pSubRect = pSubRect->next;
1093 : }
1094 :
1095 0 : CompletedRegion.MoveInto (aResult);
1096 : }
1097 0 : }
1098 :
1099 :
1100 : // Subtract rectangle from current region.
1101 : // Both region and rectangle are non-empty and they intersect each other.
1102 : // Result could be empty region if aRect fully overlays aRegion.
1103 : // Could be called repeatedly with 'this' as input and result - bound rectangle is not known.
1104 : // Optimize () is not called on exit (bound rectangle is not updated).
1105 : //
1106 : // aCompleted is filled with rectangles which are already checked and could be safely
1107 : // removed from further examination in case aRect rectangles come from ordered list.
1108 : // aCompleted is not automatically emptied. aCompleted and aResult could be the same region.
1109 :
1110 0 : void nsRegion::SubRect (const nsRectFast& aRect, nsRegion& aResult, nsRegion& aCompleted) const
1111 : {
1112 0 : nsRegion TmpRegion;
1113 0 : const nsRegion* pSrcRegion = this;
1114 :
1115 0 : if (&aResult == this) // Copy region if it is both source and result
1116 : {
1117 0 : TmpRegion.Copy (*this);
1118 0 : pSrcRegion = &TmpRegion;
1119 : }
1120 :
1121 0 : aResult.SetToElements (0);
1122 :
1123 0 : const_cast<nsRegion*>(pSrcRegion)->mRectListHead.y = NS_COORD_GREATER_SENTINEL;
1124 0 : const RgnRect* pSrcRect = pSrcRegion->mRectListHead.next;
1125 :
1126 0 : for ( ; pSrcRect->y < aRect.YMost () ; pSrcRect = pSrcRect->next)
1127 : {
1128 0 : nsRectFast TmpRect;
1129 :
1130 : // If bottom of current rectangle is above the top of aRect then this rectangle
1131 : // could be moved to aCompleted region. Successive aRect rectangles from ordered
1132 : // list do not have to check this rectangle again.
1133 0 : if (pSrcRect->YMost () <= aRect.y)
1134 : {
1135 0 : aCompleted.InsertInPlace (new RgnRect (*pSrcRect));
1136 0 : continue;
1137 : }
1138 :
1139 0 : if (!TmpRect.IntersectRect (*pSrcRect, aRect))
1140 0 : aResult.InsertInPlace (new RgnRect (*pSrcRect));
1141 : else
1142 : {
1143 : // Rectangle A. Subtract from this rectangle B
1144 0 : const nscoord ax = pSrcRect->x;
1145 0 : const nscoord axm = pSrcRect->XMost ();
1146 0 : const nscoord aw = pSrcRect->width;
1147 0 : const nscoord ay = pSrcRect->y;
1148 0 : const nscoord aym = pSrcRect->YMost ();
1149 0 : const nscoord ah = pSrcRect->height;
1150 : // Rectangle B. Subtract this from rectangle A
1151 0 : const nscoord bx = aRect.x;
1152 0 : const nscoord bxm = aRect.XMost ();
1153 0 : const nscoord by = aRect.y;
1154 0 : const nscoord bym = aRect.YMost ();
1155 : // Rectangle I. Area where rectangles A and B intersect
1156 0 : const nscoord ix = TmpRect.x;
1157 0 : const nscoord ixm = TmpRect.XMost ();
1158 0 : const nscoord iy = TmpRect.y;
1159 0 : const nscoord iym = TmpRect.YMost ();
1160 0 : const nscoord ih = TmpRect.height;
1161 :
1162 : // There are 16 combinations how rectangles could intersect
1163 :
1164 0 : if (bx <= ax && by <= ay)
1165 : {
1166 0 : if (bxm < axm && bym < aym) // 1.
1167 : {
1168 0 : aResult.InsertInPlace (new RgnRect (ixm, ay, axm - ixm, ih));
1169 0 : aResult.InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
1170 : } else
1171 0 : if (bxm >= axm && bym < aym) // 2.
1172 : {
1173 0 : aResult.InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
1174 : } else
1175 0 : if (bxm < axm && bym >= aym) // 3.
1176 : {
1177 0 : aResult.InsertInPlace (new RgnRect (ixm, ay, axm - ixm, ah));
1178 : } else
1179 0 : if (pSrcRect->IsEqualInterior(aRect)) // 4. subset
1180 : { // Current rectangle is equal to aRect
1181 0 : pSrcRect = pSrcRect->next; // don't add this one to the result, it's removed
1182 : break; // No any other rectangle in region can intersect it
1183 : }
1184 : } else
1185 0 : if (bx > ax && by <= ay)
1186 : {
1187 0 : if (bxm < axm && bym < aym) // 5.
1188 : {
1189 0 : aResult.InsertInPlace (new RgnRect (ax, ay, ix - ax, ih));
1190 0 : aResult.InsertInPlace (new RgnRect (ixm, ay, axm - ixm, ih));
1191 0 : aResult.InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
1192 : } else
1193 0 : if (bxm >= axm && bym < aym) // 6.
1194 : {
1195 0 : aResult.InsertInPlace (new RgnRect (ax, ay, ix - ax, ih));
1196 0 : aResult.InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
1197 : } else
1198 0 : if (bxm < axm && bym >= aym) // 7.
1199 : {
1200 0 : aResult.InsertInPlace (new RgnRect (ax, ay, ix - ax, ah));
1201 0 : aResult.InsertInPlace (new RgnRect (ixm, ay, axm - ixm, ah));
1202 : } else
1203 0 : if (bxm >= axm && bym >= aym) // 8.
1204 : {
1205 0 : aResult.InsertInPlace (new RgnRect (ax, ay, ix - ax, ah));
1206 : }
1207 : } else
1208 0 : if (bx <= ax && by > ay)
1209 : {
1210 0 : if (bxm < axm && bym < aym) // 9.
1211 : {
1212 0 : aResult.InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
1213 0 : aResult.InsertInPlace (new RgnRect (ixm, iy, axm - ixm, ih));
1214 0 : aResult.InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
1215 : } else
1216 0 : if (bxm >= axm && bym < aym) // 10.
1217 : {
1218 0 : aResult.InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
1219 0 : aResult.InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
1220 : } else
1221 0 : if (bxm < axm && bym >= aym) // 11.
1222 : {
1223 0 : aResult.InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
1224 0 : aResult.InsertInPlace (new RgnRect (ixm, iy, axm - ixm, ih));
1225 : } else
1226 0 : if (bxm >= axm && bym >= aym) // 12.
1227 : {
1228 0 : aResult.InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
1229 : }
1230 : } else
1231 0 : if (bx > ax && by > ay)
1232 : {
1233 0 : if (bxm < axm && bym < aym) // 13.
1234 : {
1235 0 : aResult.InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
1236 0 : aResult.InsertInPlace (new RgnRect (ax, iy, ix - ax, ih));
1237 0 : aResult.InsertInPlace (new RgnRect (ixm, iy, axm - ixm, ih));
1238 0 : aResult.InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
1239 :
1240 : // Current rectangle fully overlays aRect. No any other rectangle can intersect it.
1241 0 : pSrcRect = pSrcRect->next; // don't add this one to the result, it's removed
1242 : break;
1243 : } else
1244 0 : if (bxm >= axm && bym < aym) // 14.
1245 : {
1246 0 : aResult.InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
1247 0 : aResult.InsertInPlace (new RgnRect (ax, iy, ix - ax, ih));
1248 0 : aResult.InsertInPlace (new RgnRect (ax, iym, aw, aym - iym));
1249 : } else
1250 0 : if (bxm < axm && bym >= aym) // 15.
1251 : {
1252 0 : aResult.InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
1253 0 : aResult.InsertInPlace (new RgnRect (ax, iy, ix - ax, ih));
1254 0 : aResult.InsertInPlace (new RgnRect (ixm, iy, axm - ixm, ih));
1255 : } else
1256 0 : if (bxm >= axm && bym >= aym) // 16.
1257 : {
1258 0 : aResult.InsertInPlace (new RgnRect (ax, ay, aw, iy - ay));
1259 0 : aResult.InsertInPlace (new RgnRect (ax, iy, ix - ax, ih));
1260 : }
1261 : }
1262 : }
1263 : }
1264 :
1265 : // Just copy remaining rectangles in region which are below aRect and can't intersect it.
1266 : // If rectangles are in temporary region then they could be moved.
1267 0 : if (pSrcRegion == &TmpRegion)
1268 0 : TmpRegion.MoveInto (aResult, pSrcRect);
1269 : else
1270 : {
1271 0 : while (pSrcRect != &pSrcRegion->mRectListHead)
1272 : {
1273 0 : aResult.InsertInPlace (new RgnRect (*pSrcRect));
1274 0 : pSrcRect = pSrcRect->next;
1275 : }
1276 : }
1277 0 : }
1278 :
1279 :
1280 0 : bool nsRegion::IsEqual (const nsRegion& aRegion) const
1281 : {
1282 0 : if (mRectCount == 0)
1283 0 : return (aRegion.mRectCount == 0) ? true : false;
1284 :
1285 0 : if (aRegion.mRectCount == 0)
1286 0 : return (mRectCount == 0) ? true : false;
1287 :
1288 0 : if (mRectCount == 1 && aRegion.mRectCount == 1) // Both regions are simple rectangles
1289 0 : return (mRectListHead.next->IsEqualInterior(*aRegion.mRectListHead.next));
1290 : else // At least one is complex region.
1291 : {
1292 0 : if (!mBoundRect.IsEqualInterior(aRegion.mBoundRect)) // If regions are equal then bounding rectangles should match
1293 0 : return false;
1294 : else
1295 : {
1296 0 : nsRegion TmpRegion;
1297 0 : TmpRegion.Xor (*this, aRegion); // Get difference between two regions
1298 :
1299 0 : return (TmpRegion.mRectCount == 0);
1300 : }
1301 : }
1302 : }
1303 :
1304 :
1305 0 : void nsRegion::MoveBy (nsPoint aPt)
1306 : {
1307 0 : if (aPt.x || aPt.y)
1308 : {
1309 0 : RgnRect* pRect = mRectListHead.next;
1310 :
1311 0 : while (pRect != &mRectListHead)
1312 : {
1313 0 : pRect->MoveBy (aPt.x, aPt.y);
1314 0 : pRect = pRect->next;
1315 : }
1316 :
1317 0 : mBoundRect.MoveBy (aPt.x, aPt.y);
1318 : }
1319 0 : }
1320 :
1321 0 : nsRegion& nsRegion::ScaleRoundOut (float aXScale, float aYScale)
1322 : {
1323 0 : nsRegion region;
1324 0 : nsRegionRectIterator iter(*this);
1325 0 : for (;;) {
1326 0 : const nsRect* r = iter.Next();
1327 0 : if (!r)
1328 : break;
1329 0 : nsRect rect = *r;
1330 0 : rect.ScaleRoundOut(aXScale, aYScale);
1331 0 : region.Or(region, rect);
1332 : }
1333 0 : *this = region;
1334 0 : return *this;
1335 : }
1336 :
1337 0 : nsRegion& nsRegion::ScaleInverseRoundOut (float aXScale, float aYScale)
1338 : {
1339 0 : nsRegion region;
1340 0 : nsRegionRectIterator iter(*this);
1341 0 : for (;;) {
1342 0 : const nsRect* r = iter.Next();
1343 0 : if (!r)
1344 : break;
1345 0 : nsRect rect = *r;
1346 0 : rect.ScaleInverseRoundOut(aXScale, aYScale);
1347 0 : region.Or(region, rect);
1348 : }
1349 0 : *this = region;
1350 0 : return *this;
1351 : }
1352 :
1353 0 : nsRegion nsRegion::ConvertAppUnitsRoundOut (PRInt32 aFromAPP, PRInt32 aToAPP) const
1354 : {
1355 0 : if (aFromAPP == aToAPP) {
1356 0 : return *this;
1357 : }
1358 : // Do it in a simplistic and slow way to avoid any weird behaviour with
1359 : // rounding causing rects to overlap. Should be fast enough for what we need.
1360 0 : nsRegion region;
1361 0 : nsRegionRectIterator iter(*this);
1362 0 : for (;;) {
1363 0 : const nsRect* r = iter.Next();
1364 0 : if (!r)
1365 : break;
1366 0 : nsRect rect = r->ConvertAppUnitsRoundOut(aFromAPP, aToAPP);
1367 0 : region.Or(region, rect);
1368 : }
1369 0 : return region;
1370 : }
1371 :
1372 0 : nsRegion nsRegion::ConvertAppUnitsRoundIn (PRInt32 aFromAPP, PRInt32 aToAPP) const
1373 : {
1374 0 : if (aFromAPP == aToAPP) {
1375 0 : return *this;
1376 : }
1377 : // Do it in a simplistic and slow way to avoid any weird behaviour with
1378 : // rounding causing rects to overlap. Should be fast enough for what we need.
1379 0 : nsRegion region;
1380 0 : nsRegionRectIterator iter(*this);
1381 0 : for (;;) {
1382 0 : const nsRect* r = iter.Next();
1383 0 : if (!r)
1384 : break;
1385 0 : nsRect rect = r->ConvertAppUnitsRoundIn(aFromAPP, aToAPP);
1386 0 : region.Or(region, rect);
1387 : }
1388 0 : return region;
1389 : }
1390 :
1391 0 : nsIntRegion nsRegion::ToPixels (nscoord aAppUnitsPerPixel, bool aOutsidePixels) const
1392 : {
1393 0 : nsIntRegion result;
1394 0 : nsRegionRectIterator rgnIter(*this);
1395 : const nsRect* currentRect;
1396 0 : while ((currentRect = rgnIter.Next())) {
1397 0 : nsIntRect deviceRect;
1398 0 : if (aOutsidePixels)
1399 0 : deviceRect = currentRect->ToOutsidePixels(aAppUnitsPerPixel);
1400 : else
1401 0 : deviceRect = currentRect->ToNearestPixels(aAppUnitsPerPixel);
1402 0 : result.Or(result, deviceRect);
1403 : }
1404 : return result;
1405 : }
1406 :
1407 0 : nsIntRegion nsRegion::ToOutsidePixels (nscoord aAppUnitsPerPixel) const
1408 : {
1409 0 : return ToPixels(aAppUnitsPerPixel, true);
1410 : }
1411 :
1412 0 : nsIntRegion nsRegion::ToNearestPixels (nscoord aAppUnitsPerPixel) const
1413 : {
1414 0 : return ToPixels(aAppUnitsPerPixel, false);
1415 : }
1416 :
1417 0 : nsIntRegion nsRegion::ScaleToOutsidePixels (float aScaleX, float aScaleY,
1418 : nscoord aAppUnitsPerPixel) const
1419 : {
1420 0 : nsIntRegion result;
1421 0 : nsRegionRectIterator rgnIter(*this);
1422 : const nsRect* currentRect;
1423 0 : while ((currentRect = rgnIter.Next())) {
1424 : nsIntRect deviceRect =
1425 0 : currentRect->ScaleToOutsidePixels(aScaleX, aScaleY, aAppUnitsPerPixel);
1426 0 : result.Or(result, deviceRect);
1427 : }
1428 : return result;
1429 : }
1430 :
1431 : // A cell's "value" is a pair consisting of
1432 : // a) the area of the subrectangle it corresponds to, if it's in
1433 : // aContainingRect and in the region, 0 otherwise
1434 : // b) the area of the subrectangle it corresponds to, if it's in the region,
1435 : // 0 otherwise
1436 : // Addition, subtraction and identity are defined on these values in the
1437 : // obvious way. Partial order is lexicographic.
1438 : // A "large negative value" is defined with large negative numbers for both
1439 : // fields of the pair. This negative value has the property that adding any
1440 : // number of non-negative values to it always results in a negative value.
1441 : //
1442 : // The GetLargestRectangle algorithm works in three phases:
1443 : // 1) Convert the region into a grid by adding vertical/horizontal lines for
1444 : // each edge of each rectangle in the region.
1445 : // 2) For each rectangle in the region, for each cell it contains, set that
1446 : // cells's value as described above.
1447 : // 3) Calculate the submatrix with the largest sum such that none of its cells
1448 : // contain any 0s (empty regions). The rectangle represented by the
1449 : // submatrix is the largest rectangle in the region.
1450 : //
1451 : // Let k be the number of rectangles in the region.
1452 : // Let m be the height of the grid generated in step 1.
1453 : // Let n be the width of the grid generated in step 1.
1454 : //
1455 : // Step 1 is O(k) in time and O(m+n) in space for the sparse grid.
1456 : // Step 2 is O(mn) in time and O(mn) in additional space for the full grid.
1457 : // Step 3 is O(m^2 n) in time and O(mn) in additional space
1458 : //
1459 : // The implementation of steps 1 and 2 are rather straightforward. However our
1460 : // implementation of step 3 uses dynamic programming to achieve its efficiency.
1461 : //
1462 : // Psuedo code for step 3 is as follows where G is the grid from step 1 and A
1463 : // is the array from step 2:
1464 : // Phase3 = function (G, A, m, n) {
1465 : // let (t,b,l,r,_) = MaxSum2D(A,m,n)
1466 : // return rect(G[t],G[l],G[r],G[b]);
1467 : // }
1468 : // MaxSum2D = function (A, m, n) {
1469 : // S = array(m+1,n+1)
1470 : // S[0][i] = 0 for i in [0,n]
1471 : // S[j][0] = 0 for j in [0,m]
1472 : // S[j][i] = (if A[j-1][i-1] = 0 then some large negative value else A[j-1][i-1])
1473 : // + S[j-1][n] + S[j][i-1] - S[j-1][i-1]
1474 : //
1475 : // // top, bottom, left, right, area
1476 : // var maxRect = (-1, -1, -1, -1, 0);
1477 : //
1478 : // for all (m',m'') in [0, m]^2 {
1479 : // let B = { S[m'][i] - S[m''][i] | 0 <= i <= n }
1480 : // let ((l,r),area) = MaxSum1D(B,n+1)
1481 : // if (area > maxRect.area) {
1482 : // maxRect := (m', m'', l, r, area)
1483 : // }
1484 : // }
1485 : //
1486 : // return maxRect;
1487 : // }
1488 : //
1489 : // Originally taken from Improved algorithms for the k-maximum subarray problem
1490 : // for small k - SE Bae, T Takaoka but modified to show the explicit tracking
1491 : // of indices and we already have the prefix sums from our one call site so
1492 : // there's no need to construct them.
1493 : // MaxSum1D = function (A,n) {
1494 : // var minIdx = 0;
1495 : // var min = 0;
1496 : // var maxIndices = (0,0);
1497 : // var max = 0;
1498 : // for i in range(n) {
1499 : // let cand = A[i] - min;
1500 : // if (cand > max) {
1501 : // max := cand;
1502 : // maxIndices := (minIdx, i)
1503 : // }
1504 : // if (min > A[i]) {
1505 : // min := A[i];
1506 : // minIdx := i;
1507 : // }
1508 : // }
1509 : // return (minIdx, maxIdx, max);
1510 : // }
1511 :
1512 : namespace {
1513 : // This class represents a partitioning of an axis delineated by coordinates.
1514 : // It internally maintains a sorted array of coordinates.
1515 0 : class AxisPartition {
1516 : public:
1517 : // Adds a new partition at the given coordinate to this partitioning. If
1518 : // the coordinate is already present in the partitioning, this does nothing.
1519 0 : void InsertCoord(nscoord c) {
1520 : PRUint32 i;
1521 0 : if (!mStops.GreatestIndexLtEq(c, i)) {
1522 0 : mStops.InsertElementAt(i, c);
1523 : }
1524 0 : }
1525 :
1526 : // Returns the array index of the given partition point. The partition
1527 : // point must already be present in the partitioning.
1528 0 : PRInt32 IndexOf(nscoord p) const {
1529 0 : return mStops.BinaryIndexOf(p);
1530 : }
1531 :
1532 : // Returns the partition at the given index which must be non-zero and
1533 : // less than the number of partitions in this partitioning.
1534 0 : nscoord StopAt(PRInt32 index) const {
1535 0 : return mStops[index];
1536 : }
1537 :
1538 : // Returns the size of the gap between the partition at the given index and
1539 : // the next partition in this partitioning. If the index is the last index
1540 : // in the partitioning, the result is undefined.
1541 0 : nscoord StopSize(PRInt32 index) const {
1542 0 : return mStops[index+1] - mStops[index];
1543 : }
1544 :
1545 : // Returns the number of partitions in this partitioning.
1546 0 : PRInt32 GetNumStops() const { return mStops.Length(); }
1547 :
1548 : private:
1549 : nsTArray<nscoord> mStops;
1550 : };
1551 :
1552 : const PRInt64 kVeryLargeNegativeNumber = 0xffff000000000000ll;
1553 :
1554 0 : struct SizePair {
1555 : PRInt64 mSizeContainingRect;
1556 : PRInt64 mSize;
1557 :
1558 0 : SizePair() : mSizeContainingRect(0), mSize(0) {}
1559 :
1560 0 : static SizePair VeryLargeNegative() {
1561 0 : SizePair result;
1562 0 : result.mSize = result.mSizeContainingRect = kVeryLargeNegativeNumber;
1563 : return result;
1564 : }
1565 0 : SizePair& operator=(const SizePair& aOther) {
1566 0 : mSizeContainingRect = aOther.mSizeContainingRect;
1567 0 : mSize = aOther.mSize;
1568 0 : return *this;
1569 : }
1570 0 : bool operator<(const SizePair& aOther) const {
1571 0 : if (mSizeContainingRect < aOther.mSizeContainingRect)
1572 0 : return true;
1573 0 : if (mSizeContainingRect > aOther.mSizeContainingRect)
1574 0 : return false;
1575 0 : return mSize < aOther.mSize;
1576 : }
1577 0 : bool operator>(const SizePair& aOther) const {
1578 0 : return aOther.operator<(*this);
1579 : }
1580 0 : SizePair operator+(const SizePair& aOther) const {
1581 0 : SizePair result = *this;
1582 0 : result.mSizeContainingRect += aOther.mSizeContainingRect;
1583 0 : result.mSize += aOther.mSize;
1584 : return result;
1585 : }
1586 0 : SizePair operator-(const SizePair& aOther) const {
1587 0 : SizePair result = *this;
1588 0 : result.mSizeContainingRect -= aOther.mSizeContainingRect;
1589 0 : result.mSize -= aOther.mSize;
1590 : return result;
1591 : }
1592 : };
1593 :
1594 : // Returns the sum and indices of the subarray with the maximum sum of the
1595 : // given array (A,n), assuming the array is already in prefix sum form.
1596 0 : SizePair MaxSum1D(const nsTArray<SizePair> &A, PRInt32 n,
1597 : PRInt32 *minIdx, PRInt32 *maxIdx) {
1598 : // The min/max indicies of the largest subarray found so far
1599 0 : SizePair min, max;
1600 0 : PRInt32 currentMinIdx = 0;
1601 :
1602 0 : *minIdx = 0;
1603 0 : *maxIdx = 0;
1604 :
1605 : // Because we're given the array in prefix sum form, we know the first
1606 : // element is 0
1607 0 : for(PRInt32 i = 1; i < n; i++) {
1608 0 : SizePair cand = A[i] - min;
1609 0 : if (cand > max) {
1610 0 : max = cand;
1611 0 : *minIdx = currentMinIdx;
1612 0 : *maxIdx = i;
1613 : }
1614 0 : if (min > A[i]) {
1615 0 : min = A[i];
1616 0 : currentMinIdx = i;
1617 : }
1618 : }
1619 :
1620 : return max;
1621 : }
1622 : }
1623 :
1624 0 : nsRect nsRegion::GetLargestRectangle (const nsRect& aContainingRect) const {
1625 0 : nsRect bestRect;
1626 :
1627 0 : if (mRectCount <= 1) {
1628 0 : bestRect = mBoundRect;
1629 0 : return bestRect;
1630 : }
1631 :
1632 0 : AxisPartition xaxis, yaxis;
1633 :
1634 : // Step 1: Calculate the grid lines
1635 0 : nsRegionRectIterator iter(*this);
1636 : const nsRect *currentRect;
1637 0 : while ((currentRect = iter.Next())) {
1638 0 : xaxis.InsertCoord(currentRect->x);
1639 0 : xaxis.InsertCoord(currentRect->XMost());
1640 0 : yaxis.InsertCoord(currentRect->y);
1641 0 : yaxis.InsertCoord(currentRect->YMost());
1642 : }
1643 0 : if (!aContainingRect.IsEmpty()) {
1644 0 : xaxis.InsertCoord(aContainingRect.x);
1645 0 : xaxis.InsertCoord(aContainingRect.XMost());
1646 0 : yaxis.InsertCoord(aContainingRect.y);
1647 0 : yaxis.InsertCoord(aContainingRect.YMost());
1648 : }
1649 :
1650 : // Step 2: Fill out the grid with the areas
1651 : // Note: due to the ordering of rectangles in the region, it is not always
1652 : // possible to combine steps 2 and 3 so we don't try to be clever.
1653 0 : PRInt32 matrixHeight = yaxis.GetNumStops() - 1;
1654 0 : PRInt32 matrixWidth = xaxis.GetNumStops() - 1;
1655 0 : PRInt32 matrixSize = matrixHeight * matrixWidth;
1656 0 : nsTArray<SizePair> areas(matrixSize);
1657 0 : areas.SetLength(matrixSize);
1658 :
1659 0 : iter.Reset();
1660 0 : while ((currentRect = iter.Next())) {
1661 0 : PRInt32 xstart = xaxis.IndexOf(currentRect->x);
1662 0 : PRInt32 xend = xaxis.IndexOf(currentRect->XMost());
1663 0 : PRInt32 y = yaxis.IndexOf(currentRect->y);
1664 0 : PRInt32 yend = yaxis.IndexOf(currentRect->YMost());
1665 :
1666 0 : for (; y < yend; y++) {
1667 0 : nscoord height = yaxis.StopSize(y);
1668 0 : for (PRInt32 x = xstart; x < xend; x++) {
1669 0 : nscoord width = xaxis.StopSize(x);
1670 0 : PRInt64 size = width*PRInt64(height);
1671 0 : if (currentRect->Intersects(aContainingRect)) {
1672 0 : areas[y*matrixWidth+x].mSizeContainingRect = size;
1673 : }
1674 0 : areas[y*matrixWidth+x].mSize = size;
1675 : }
1676 : }
1677 : }
1678 :
1679 : // Step 3: Find the maximum submatrix sum that does not contain a rectangle
1680 : {
1681 : // First get the prefix sum array
1682 0 : PRInt32 m = matrixHeight + 1;
1683 0 : PRInt32 n = matrixWidth + 1;
1684 0 : nsTArray<SizePair> pareas(m*n);
1685 0 : pareas.SetLength(m*n);
1686 0 : for (PRInt32 y = 1; y < m; y++) {
1687 0 : for (PRInt32 x = 1; x < n; x++) {
1688 0 : SizePair area = areas[(y-1)*matrixWidth+x-1];
1689 0 : if (!area.mSize) {
1690 0 : area = SizePair::VeryLargeNegative();
1691 : }
1692 0 : area = area + pareas[ y*n+x-1]
1693 0 : + pareas[(y-1)*n+x ]
1694 0 : - pareas[(y-1)*n+x-1];
1695 0 : pareas[y*n+x] = area;
1696 : }
1697 : }
1698 :
1699 : // No longer need the grid
1700 0 : areas.SetLength(0);
1701 :
1702 0 : SizePair bestArea;
1703 : struct {
1704 : PRInt32 left, top, right, bottom;
1705 0 : } bestRectIndices = { 0, 0, 0, 0 };
1706 0 : for (PRInt32 m1 = 0; m1 < m; m1++) {
1707 0 : for (PRInt32 m2 = m1+1; m2 < m; m2++) {
1708 0 : nsTArray<SizePair> B;
1709 0 : B.SetLength(n);
1710 0 : for (PRInt32 i = 0; i < n; i++) {
1711 0 : B[i] = pareas[m2*n+i] - pareas[m1*n+i];
1712 : }
1713 : PRInt32 minIdx, maxIdx;
1714 0 : SizePair area = MaxSum1D(B, n, &minIdx, &maxIdx);
1715 0 : if (area > bestArea) {
1716 0 : bestRectIndices.left = minIdx;
1717 0 : bestRectIndices.top = m1;
1718 0 : bestRectIndices.right = maxIdx;
1719 0 : bestRectIndices.bottom = m2;
1720 0 : bestArea = area;
1721 : }
1722 : }
1723 : }
1724 :
1725 : bestRect.MoveTo(xaxis.StopAt(bestRectIndices.left),
1726 0 : yaxis.StopAt(bestRectIndices.top));
1727 0 : bestRect.SizeTo(xaxis.StopAt(bestRectIndices.right) - bestRect.x,
1728 0 : yaxis.StopAt(bestRectIndices.bottom) - bestRect.y);
1729 : }
1730 :
1731 : return bestRect;
1732 : }
1733 :
1734 0 : void nsRegion::SimplifyOutward (PRUint32 aMaxRects)
1735 : {
1736 0 : NS_ASSERTION(aMaxRects >= 1, "Invalid max rect count");
1737 :
1738 0 : if (mRectCount <= aMaxRects)
1739 0 : return;
1740 :
1741 : // Try combining rects in horizontal bands into a single rect
1742 0 : RgnRect* pRect = mRectListHead.next;
1743 0 : while (pRect != &mRectListHead)
1744 : {
1745 : // Combine with the following rectangle if they have the same YMost
1746 : // or if they overlap vertically. This ensures that all overlapping
1747 : // rectangles are merged, preserving the invariant that rectangles
1748 : // don't overlap.
1749 : // The goal here is to try to keep groups of rectangles that are vertically
1750 : // discontiguous as separate rectangles in the final region. This is
1751 : // simple and fast to implement and page contents tend to vary more
1752 : // vertically than horizontally (which is why our rectangles are stored
1753 : // sorted by y-coordinate, too).
1754 0 : while (pRect->next != &mRectListHead &&
1755 0 : pRect->YMost () >= pRect->next->y)
1756 : {
1757 0 : pRect->UnionRect(*pRect, *pRect->next);
1758 0 : delete Remove (pRect->next);
1759 : }
1760 :
1761 0 : pRect = pRect->next;
1762 : }
1763 :
1764 0 : if (mRectCount <= aMaxRects)
1765 0 : return;
1766 :
1767 0 : *this = GetBounds();
1768 : }
1769 :
1770 0 : void nsRegion::SimplifyInward (PRUint32 aMaxRects)
1771 : {
1772 0 : NS_ASSERTION(aMaxRects >= 1, "Invalid max rect count");
1773 :
1774 0 : if (mRectCount <= aMaxRects)
1775 0 : return;
1776 :
1777 0 : SetEmpty();
1778 : }
1779 :
1780 0 : void nsRegion::SimpleSubtract (const nsRect& aRect)
1781 : {
1782 0 : if (aRect.IsEmpty())
1783 0 : return;
1784 :
1785 : // protect against aRect being one of our own rectangles
1786 0 : nsRect param = aRect;
1787 0 : RgnRect* r = mRectListHead.next;
1788 0 : while (r != &mRectListHead)
1789 : {
1790 0 : RgnRect* next = r->next;
1791 0 : if (param.Contains(*r)) {
1792 0 : delete Remove(r);
1793 : }
1794 0 : r = next;
1795 : }
1796 :
1797 0 : Optimize();
1798 : }
1799 :
1800 0 : void nsRegion::SimpleSubtract (const nsRegion& aRegion)
1801 : {
1802 0 : if (aRegion.IsEmpty())
1803 0 : return;
1804 :
1805 0 : if (&aRegion == this) {
1806 0 : SetEmpty();
1807 0 : return;
1808 : }
1809 :
1810 0 : const RgnRect* r = aRegion.mRectListHead.next;
1811 0 : while (r != &aRegion.mRectListHead)
1812 : {
1813 0 : SimpleSubtract(*r);
1814 0 : r = r->next;
1815 : }
1816 :
1817 0 : Optimize();
1818 : }
1819 :
1820 0 : nsRegion nsIntRegion::ToAppUnits (nscoord aAppUnitsPerPixel) const
1821 : {
1822 0 : nsRegion result;
1823 0 : nsIntRegionRectIterator rgnIter(*this);
1824 : const nsIntRect* currentRect;
1825 0 : while ((currentRect = rgnIter.Next())) {
1826 0 : nsRect appRect = currentRect->ToAppUnits(aAppUnitsPerPixel);
1827 0 : result.Or(result, appRect);
1828 : }
1829 : return result;
1830 : }
|