mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 613449. Extend GetLargestRectangle to support considering only rectangles that contain a given rectangle. r=robarnold
This commit is contained in:
parent
eb517ba4b4
commit
e7c81607af
@ -1339,12 +1339,22 @@ nsIntRegion nsRegion::ToOutsidePixels (nscoord aAppUnitsPerPixel) const
|
||||
return result;
|
||||
}
|
||||
|
||||
// This algorithm works in three phases:
|
||||
// A cell's "value" is a pair consisting of
|
||||
// a) the area of the subrectangle it corresponds to, if it's in
|
||||
// aContainingRect and in the region, 0 otherwise
|
||||
// b) the area of the subrectangle it corresponds to, if it's in the region,
|
||||
// 0 otherwise
|
||||
// Addition, subtraction and identity are defined on these values in the
|
||||
// obvious way. Partial order is lexicographic.
|
||||
// A "large negative value" is defined with large negative numbers for both
|
||||
// fields of the pair. This negative value has the property that adding any
|
||||
// number of non-negative values to it always results in a negative value.
|
||||
//
|
||||
// The GetLargestRectangle algorithm works in three phases:
|
||||
// 1) Convert the region into a grid by adding vertical/horizontal lines for
|
||||
// each edge of each rectangle in the region.
|
||||
// 2) For each rectangle in the region, for each cell it contains, set that
|
||||
// cells's value to the area of the subrectangle it corresponds to. Cells
|
||||
// that are not contained by any rectangle have the value 0.
|
||||
// cells's value as described above.
|
||||
// 3) Calculate the submatrix with the largest sum such that none of its cells
|
||||
// contain any 0s (empty regions). The rectangle represented by the
|
||||
// submatrix is the largest rectangle in the region.
|
||||
@ -1370,7 +1380,7 @@ nsIntRegion nsRegion::ToOutsidePixels (nscoord aAppUnitsPerPixel) const
|
||||
// S = array(m+1,n+1)
|
||||
// S[0][i] = 0 for i in [0,n]
|
||||
// S[j][0] = 0 for j in [0,m]
|
||||
// S[j][i] = (if A[j-1][i-1] = 0 then some large negative number else A[j-1][i-1])
|
||||
// S[j][i] = (if A[j-1][i-1] = 0 then some large negative value else A[j-1][i-1])
|
||||
// + S[j-1][n] + S[j][i-1] - S[j-1][i-1]
|
||||
//
|
||||
// // top, bottom, left, right, area
|
||||
@ -1452,13 +1462,52 @@ namespace {
|
||||
|
||||
const PRInt64 kVeryLargeNegativeNumber = 0xffff000000000000ll;
|
||||
|
||||
struct SizePair {
|
||||
PRInt64 mSizeContainingRect;
|
||||
PRInt64 mSize;
|
||||
|
||||
SizePair() : mSizeContainingRect(0), mSize(0) {}
|
||||
|
||||
static SizePair VeryLargeNegative() {
|
||||
SizePair result;
|
||||
result.mSize = result.mSizeContainingRect = kVeryLargeNegativeNumber;
|
||||
return result;
|
||||
}
|
||||
SizePair& operator=(const SizePair& aOther) {
|
||||
mSizeContainingRect = aOther.mSizeContainingRect;
|
||||
mSize = aOther.mSize;
|
||||
return *this;
|
||||
}
|
||||
PRBool operator<(const SizePair& aOther) const {
|
||||
if (mSizeContainingRect < aOther.mSizeContainingRect)
|
||||
return PR_TRUE;
|
||||
if (mSizeContainingRect > aOther.mSizeContainingRect)
|
||||
return PR_FALSE;
|
||||
return mSize < aOther.mSize;
|
||||
}
|
||||
PRBool operator>(const SizePair& aOther) const {
|
||||
return aOther.operator<(*this);
|
||||
}
|
||||
SizePair operator+(const SizePair& aOther) const {
|
||||
SizePair result = *this;
|
||||
result.mSizeContainingRect += aOther.mSizeContainingRect;
|
||||
result.mSize += aOther.mSize;
|
||||
return result;
|
||||
}
|
||||
SizePair operator-(const SizePair& aOther) const {
|
||||
SizePair result = *this;
|
||||
result.mSizeContainingRect -= aOther.mSizeContainingRect;
|
||||
result.mSize -= aOther.mSize;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// Returns the sum and indices of the subarray with the maximum sum of the
|
||||
// given array (A,n), assuming the array is already in prefix sum form.
|
||||
PRInt64 MaxSum1D(const nsTArray<PRInt64> &A, PRInt32 n,
|
||||
SizePair MaxSum1D(const nsTArray<SizePair> &A, PRInt32 n,
|
||||
PRInt32 *minIdx, PRInt32 *maxIdx) {
|
||||
// The min/max indicies of the largest subarray found so far
|
||||
PRInt64 min = 0,
|
||||
max = 0;
|
||||
SizePair min, max;
|
||||
PRInt32 currentMinIdx = 0;
|
||||
|
||||
*minIdx = 0;
|
||||
@ -1467,7 +1516,7 @@ namespace {
|
||||
// Because we're given the array in prefix sum form, we know the first
|
||||
// element is 0
|
||||
for(PRInt32 i = 1; i < n; i++) {
|
||||
PRInt64 cand = A[i] - min;
|
||||
SizePair cand = A[i] - min;
|
||||
if (cand > max) {
|
||||
max = cand;
|
||||
*minIdx = currentMinIdx;
|
||||
@ -1483,7 +1532,7 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
nsRect nsRegion::GetLargestRectangle () const {
|
||||
nsRect nsRegion::GetLargestRectangle (const nsRect& aContainingRect) const {
|
||||
nsRect bestRect;
|
||||
|
||||
if (mRectCount <= 1) {
|
||||
@ -1502,6 +1551,12 @@ nsRect nsRegion::GetLargestRectangle () const {
|
||||
yaxis.InsertCoord(currentRect->y);
|
||||
yaxis.InsertCoord(currentRect->YMost());
|
||||
}
|
||||
if (!aContainingRect.IsEmpty()) {
|
||||
xaxis.InsertCoord(aContainingRect.x);
|
||||
xaxis.InsertCoord(aContainingRect.XMost());
|
||||
yaxis.InsertCoord(aContainingRect.y);
|
||||
yaxis.InsertCoord(aContainingRect.YMost());
|
||||
}
|
||||
|
||||
// Step 2: Fill out the grid with the areas
|
||||
// Note: due to the ordering of rectangles in the region, it is not always
|
||||
@ -1509,9 +1564,8 @@ nsRect nsRegion::GetLargestRectangle () const {
|
||||
PRInt32 matrixHeight = yaxis.GetNumStops() - 1;
|
||||
PRInt32 matrixWidth = xaxis.GetNumStops() - 1;
|
||||
PRInt32 matrixSize = matrixHeight * matrixWidth;
|
||||
nsTArray<PRInt64> areas(matrixSize);
|
||||
nsTArray<SizePair> areas(matrixSize);
|
||||
areas.SetLength(matrixSize);
|
||||
memset(areas.Elements(), 0, matrixSize * sizeof(PRInt64));
|
||||
|
||||
iter.Reset();
|
||||
while ((currentRect = iter.Next())) {
|
||||
@ -1524,7 +1578,11 @@ nsRect nsRegion::GetLargestRectangle () const {
|
||||
nscoord height = yaxis.StopSize(y);
|
||||
for (PRInt32 x = xstart; x < xend; x++) {
|
||||
nscoord width = xaxis.StopSize(x);
|
||||
areas[y*matrixWidth+x] = width*PRInt64(height);
|
||||
PRInt64 size = width*PRInt64(height);
|
||||
if (currentRect->Intersects(aContainingRect)) {
|
||||
areas[y*matrixWidth+x].mSizeContainingRect = size;
|
||||
}
|
||||
areas[y*matrixWidth+x].mSize = size;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1534,19 +1592,15 @@ nsRect nsRegion::GetLargestRectangle () const {
|
||||
// First get the prefix sum array
|
||||
PRInt32 m = matrixHeight + 1;
|
||||
PRInt32 n = matrixWidth + 1;
|
||||
nsTArray<PRInt64> pareas(m*n);
|
||||
nsTArray<SizePair> pareas(m*n);
|
||||
pareas.SetLength(m*n);
|
||||
// Zero out the first row
|
||||
for (PRInt32 x = 0; x < n; x++)
|
||||
pareas[x] = 0;
|
||||
for (PRInt32 y = 1; y < m; y++) {
|
||||
// Zero out the left column
|
||||
pareas[y*n] = 0;
|
||||
for (PRInt32 x = 1; x < n; x++) {
|
||||
PRInt64 area = areas[(y-1)*matrixWidth+x-1];
|
||||
if (!area)
|
||||
area = kVeryLargeNegativeNumber;
|
||||
area += pareas[ y*n+x-1]
|
||||
SizePair area = areas[(y-1)*matrixWidth+x-1];
|
||||
if (!area.mSize) {
|
||||
area = SizePair::VeryLargeNegative();
|
||||
}
|
||||
area = area + pareas[ y*n+x-1]
|
||||
+ pareas[(y-1)*n+x ]
|
||||
- pareas[(y-1)*n+x-1];
|
||||
pareas[y*n+x] = area;
|
||||
@ -1556,18 +1610,19 @@ nsRect nsRegion::GetLargestRectangle () const {
|
||||
// No longer need the grid
|
||||
areas.SetLength(0);
|
||||
|
||||
PRInt64 bestArea = 0;
|
||||
SizePair bestArea;
|
||||
struct {
|
||||
PRInt32 left, top, right, bottom;
|
||||
} bestRectIndices = { 0, 0, 0, 0 };
|
||||
for (PRInt32 m1 = 0; m1 < m; m1++) {
|
||||
for (PRInt32 m2 = m1+1; m2 < m; m2++) {
|
||||
nsTArray<PRInt64> B;
|
||||
nsTArray<SizePair> B;
|
||||
B.SetLength(n);
|
||||
for (PRInt32 i = 0; i < n; i++)
|
||||
for (PRInt32 i = 0; i < n; i++) {
|
||||
B[i] = pareas[m2*n+i] - pareas[m1*n+i];
|
||||
}
|
||||
PRInt32 minIdx, maxIdx;
|
||||
PRInt64 area = MaxSum1D(B, n, &minIdx, &maxIdx);
|
||||
SizePair area = MaxSum1D(B, n, &minIdx, &maxIdx);
|
||||
if (area > bestArea) {
|
||||
bestRectIndices.left = minIdx;
|
||||
bestRectIndices.top = m1;
|
||||
|
@ -184,7 +184,13 @@ public:
|
||||
nsRegion ConvertAppUnitsRoundOut (PRInt32 aFromAPP, PRInt32 aToAPP) const;
|
||||
nsRegion ConvertAppUnitsRoundIn (PRInt32 aFromAPP, PRInt32 aToAPP) const;
|
||||
nsIntRegion ToOutsidePixels (nscoord aAppUnitsPerPixel) const;
|
||||
nsRect GetLargestRectangle () const;
|
||||
/**
|
||||
* Gets the largest rectangle contained in the region.
|
||||
* @param aContainingRect if non-empty, we choose a rectangle that
|
||||
* maximizes the area intersecting with aContainingRect (and break ties by
|
||||
* then choosing the largest rectangle overall)
|
||||
*/
|
||||
nsRect GetLargestRectangle (const nsRect& aContainingRect = nsRect()) const;
|
||||
|
||||
/**
|
||||
* Make sure the region has at most aMaxRects by adding area to it
|
||||
@ -421,7 +427,10 @@ public:
|
||||
PRUint32 GetNumRects () const { return mImpl.GetNumRects (); }
|
||||
nsIntRect GetBounds () const { return FromRect (mImpl.GetBounds ()); }
|
||||
nsRegion ToAppUnits (nscoord aAppUnitsPerPixel) const;
|
||||
nsIntRect GetLargestRectangle () const { return FromRect (mImpl.GetLargestRectangle()); }
|
||||
nsIntRect GetLargestRectangle (const nsIntRect& aContainingRect = nsIntRect()) const
|
||||
{
|
||||
return FromRect (mImpl.GetLargestRectangle( ToRect(aContainingRect) ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the region has at most aMaxRects by adding area to it
|
||||
|
@ -131,6 +131,24 @@ class TestLargestRegion {
|
||||
}
|
||||
return success;
|
||||
}
|
||||
static PRBool TestContainsSpecifiedRect() {
|
||||
nsRegion r(nsRect(0, 0, 100, 100));
|
||||
r.Or(r, nsRect(0, 300, 50, 50));
|
||||
if (r.GetLargestRectangle(nsRect(0, 300, 10, 10)) != nsRect(0, 300, 50, 50)) {
|
||||
fail("Chose wrong rectangle");
|
||||
return PR_FALSE;
|
||||
}
|
||||
return PR_TRUE;
|
||||
}
|
||||
static PRBool TestContainsSpecifiedOverflowingRect() {
|
||||
nsRegion r(nsRect(0, 0, 100, 100));
|
||||
r.Or(r, nsRect(0, 300, 50, 50));
|
||||
if (r.GetLargestRectangle(nsRect(0, 290, 10, 20)) != nsRect(0, 300, 50, 50)) {
|
||||
fail("Chose wrong rectangle");
|
||||
return PR_FALSE;
|
||||
}
|
||||
return PR_TRUE;
|
||||
}
|
||||
public:
|
||||
static PRBool Test() {
|
||||
if (!TestSingleRect(nsRect(0, 52, 720, 480)) ||
|
||||
@ -143,6 +161,10 @@ public:
|
||||
return PR_FALSE;
|
||||
if (!TwoRectTest())
|
||||
return PR_FALSE;
|
||||
if (!TestContainsSpecifiedRect())
|
||||
return PR_FALSE;
|
||||
if (!TestContainsSpecifiedOverflowingRect())
|
||||
return PR_FALSE;
|
||||
passed("TestLargestRegion");
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user