/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef MOZILLA_GFX_TILEDREGION_H_ #define MOZILLA_GFX_TILEDREGION_H_ #include "mozilla/ArrayView.h" #include "mozilla/gfx/Rect.h" #include "mozilla/Move.h" #include "nsRegion.h" #include "pixman.h" namespace mozilla { namespace gfx { // See TiledRegion.cpp for documentation on TiledRegionImpl. class TiledRegionImpl { public: void Clear() { mRects.Clear(); } bool AddRect(const pixman_box32_t& aRect); bool Intersects(const pixman_box32_t& aRect) const; bool Contains(const pixman_box32_t& aRect) const; operator ArrayView() const { return ArrayView(mRects); } private: nsTArray mRects; }; /** * A auto-simplifying region type that supports one rectangle per tile. * The virtual tile grid is anchored at (0, 0) and has quadratic tiles whose * size is hard-coded as kTileSize in TiledRegion.cpp. * A TiledRegion starts out empty. You can add rectangles or (regular) regions * into it by calling Add(). Add() is a mutating union operation (similar to * OrWith on nsRegion) that's *not* exact, because it will enlarge the region as * necessary to satisfy the "one rectangle per tile" requirement. * Tiled regions convert implicitly to the underlying regular region type. * The only way to remove parts from a TiledRegion is by calling SetEmpty(). */ template class TiledRegion { public: typedef typename RegionT::RectType RectT; TiledRegion() : mCoversBounds(false) {} TiledRegion(const TiledRegion& aOther) : mBounds(aOther.mBounds) , mImpl(aOther.mImpl) , mCoversBounds(false) {} TiledRegion(TiledRegion&& aOther) : mBounds(aOther.mBounds) , mImpl(Move(aOther.mImpl)) , mCoversBounds(false) {} RegionT GetRegion() const { if (mBounds.IsEmpty()) { return RegionT(); } if (mCoversBounds) { // Rect limit hit or allocation failed, treat as 1 rect. return RegionT(mBounds); } return RegionT(mImpl); } TiledRegion& operator=(const TiledRegion& aOther) { if (&aOther != this) { mBounds = aOther.mBounds; mImpl = aOther.mImpl; mCoversBounds = aOther.mCoversBounds; } return *this; } void Add(const RectT& aRect) { if (aRect.IsEmpty()) { return; } Maybe newBounds = mBounds.SafeUnion(aRect); if (!newBounds) { return; } mBounds = newBounds.value(); MOZ_ASSERT(!mBounds.Overflows()); if (mCoversBounds) { return; } if (!mImpl.AddRect(RectToBox(aRect))) { FallBackToBounds(); } } void Add(const RegionT& aRegion) { Maybe newBounds = mBounds.SafeUnion(aRegion.GetBounds()); if (!newBounds) { return; } mBounds = newBounds.value(); MOZ_ASSERT(!mBounds.Overflows()); if (mCoversBounds) { return; } for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) { RectT r = iter.Get(); if (r.IsEmpty() || r.Overflows()) { // This can happen if e.g. a negative-width rect was wrapped into a // region. Treat it the same as we would if such a rect was passed to // the Add(const RectT&) function. continue; } if (!mImpl.AddRect(RectToBox(r))) { FallBackToBounds(); return; } } } bool IsEmpty() const { return mBounds.IsEmpty(); } void SetEmpty() { mBounds.SetEmpty(); mImpl.Clear(); mCoversBounds = false; } RectT GetBounds() const { return mBounds; } bool CoversBounds() const { return mCoversBounds; } bool Intersects(const RectT& aRect) const { if (aRect.IsEmpty()) { return true; } if (aRect.Overflows() || !mBounds.Intersects(aRect)) { return false; } if (mCoversBounds) { return true; } return mImpl.Intersects(RectToBox(aRect)); } bool Contains(const RectT& aRect) const { if (aRect.IsEmpty()) { return true; } if (aRect.Overflows() || !mBounds.Contains(aRect)) { return false; } if (mCoversBounds) { return true; } return mImpl.Contains(RectToBox(aRect)); } private: void FallBackToBounds() { mCoversBounds = true; mImpl.Clear(); } static pixman_box32_t RectToBox(const RectT& aRect) { MOZ_ASSERT(!aRect.IsEmpty()); MOZ_ASSERT(!aRect.Overflows()); return { aRect.x, aRect.y, aRect.XMost(), aRect.YMost() }; } RectT mBounds; TiledRegionImpl mImpl; // mCoversBounds is true if we bailed out due to a large number of tiles. // mCoversBounds being true means that this TiledRegion is just a simple // rectangle (our mBounds). // Once set to true, the TiledRegion will stay in this state until SetEmpty // is called. bool mCoversBounds; }; typedef TiledRegion TiledIntRegion; } // namespace gfx } // namespace mozilla #endif /* MOZILLA_GFX_TILEDREGION_H_ */