LCOV - code coverage report
Current view: directory - gfx/src - nsRegion.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 856 146 17.1 %
Date: 2012-06-02 Functions: 78 25 32.1 %

       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                 : }

Generated by: LCOV version 1.7