208 lines
5.1 KiB
C++
208 lines
5.1 KiB
C++
/* -*- 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<pixman_box32_t>() const { return ArrayView<pixman_box32_t>(mRects); }
|
|
|
|
private:
|
|
nsTArray<pixman_box32_t> 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<typename RegionT>
|
|
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<RectT> 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<RectT> 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<IntRegion> TiledIntRegion;
|
|
|
|
} // namespace gfx
|
|
} // namespace mozilla
|
|
|
|
#endif /* MOZILLA_GFX_TILEDREGION_H_ */
|