feat(region): add SRgnGetRectsf

This commit is contained in:
Adam Heinermann 2025-04-20 19:56:40 -07:00 committed by fallenoak
parent f6d4a93652
commit e1587a932c
3 changed files with 257 additions and 0 deletions

View file

@ -5,8 +5,20 @@
#include "storm/Thread.hpp" #include "storm/Thread.hpp"
#include <limits> #include <limits>
static TSExportTableSyncReuse<RGN, HSRGN, HLOCKEDRGN, CCritSect> s_rgntable; static TSExportTableSyncReuse<RGN, HSRGN, HLOCKEDRGN, CCritSect> s_rgntable;
void DeleteCombinedRect(TSGrowableArray<RECTF>* combinedArray, uint32_t index);
void DeleteRect(RECTF* rect);
int32_t IsNullRect(RECTF* rect);
void AddCombinedRect(TSGrowableArray<RECTF>* combinedArray, const RECTF* rect) {
RECTF* newRect = combinedArray->New();
*newRect = *rect;
}
void AddSourceRect(TSGrowableArray<SOURCE>* sourceArray, const RECTF* rect, void* param, int32_t sequence, uint32_t flags) { void AddSourceRect(TSGrowableArray<SOURCE>* sourceArray, const RECTF* rect, void* param, int32_t sequence, uint32_t flags) {
auto source = sourceArray->New(); auto source = sourceArray->New();
@ -23,6 +35,72 @@ int32_t CheckForIntersection(const RECTF* sourceRect, const RECTF* targetRect) {
&& sourceRect->top > targetRect->bottom; && sourceRect->top > targetRect->bottom;
} }
void CombineRectangles(TSGrowableArray<RECTF>* combinedArray) {
for (uint32_t i = 1; i < combinedArray->Count(); i++) {
for (uint32_t j = 0; j < i; j++) {
RECTF* rctA = &(*combinedArray)[i];
RECTF* rctB = &(*combinedArray)[j];
if (rctA->left == rctB->left && rctA->right == rctB->right) {
if (rctA->bottom == rctB->top || rctB->bottom == rctA->top) {
rctA->bottom = std::min(rctB->bottom, rctA->bottom);
rctA->top = std::max(rctB->top, rctA->top);
DeleteRect(rctB);
break;
}
}
if (rctA->left == rctB->right || rctB->left == rctA->right) {
if (rctA->bottom == rctB->bottom && rctA->top == rctB->top) {
rctA->left = std::min(rctB->left, rctA->left);
rctA->right = std::max(rctB->right, rctA->right);
DeleteRect(rctB);
break;
}
}
if (rctA->left == rctB->right || rctB->left == rctA->right) {
if (rctA->bottom < rctB->top && rctB->bottom < rctA->top) {
RECTF newrect[5];
newrect[0].left = rctA->left;
newrect[0].bottom = rctA->bottom;
newrect[0].right = rctA->right;
newrect[0].top = rctB->bottom;
newrect[1].left = rctB->left;
newrect[1].bottom = rctB->bottom;
newrect[1].right = rctB->right;
newrect[1].top = rctA->bottom;
newrect[2].left = rctA->left;
newrect[2].bottom = rctB->top;
newrect[2].right = rctA->right;
newrect[2].top = rctA->top;
newrect[3].left = rctB->left;
newrect[3].bottom = rctA->top;
newrect[3].right = rctB->right;
newrect[3].top = rctB->top;
newrect[4].left = std::min(rctB->left, rctA->left);
newrect[4].bottom = std::max(rctB->bottom, rctA->bottom);
newrect[4].right = std::max(rctB->right, rctA->right);
newrect[4].top = std::min(rctB->top, rctA->top);
for (uint32_t k = 0; k < 5; k++) {
if (!IsNullRect(&newrect[k])) {
AddCombinedRect(combinedArray, &newrect[k]);
}
}
DeleteCombinedRect(combinedArray, i);
DeleteCombinedRect(combinedArray, j);
break;
}
}
}
}
}
int32_t CompareRects(const RECTF* rect1, const RECTF* rect2) { int32_t CompareRects(const RECTF* rect1, const RECTF* rect2) {
return rect1->left == rect2->left return rect1->left == rect2->left
&& rect1->bottom == rect2->bottom && rect1->bottom == rect2->bottom
@ -30,6 +108,10 @@ int32_t CompareRects(const RECTF* rect1, const RECTF* rect2) {
&& rect1->top == rect2->top; && rect1->top == rect2->top;
} }
void DeleteCombinedRect(TSGrowableArray<RECTF>* combinedArray, uint32_t index) {
DeleteRect(&(*combinedArray)[index]);
}
void DeleteRect(RECTF* rect) { void DeleteRect(RECTF* rect) {
rect->left = std::numeric_limits<float>::max(); rect->left = std::numeric_limits<float>::max();
rect->bottom = std::numeric_limits<float>::max(); rect->bottom = std::numeric_limits<float>::max();
@ -37,6 +119,49 @@ void DeleteRect(RECTF* rect) {
rect->top = std::numeric_limits<float>::max(); rect->top = std::numeric_limits<float>::max();
} }
void FragmentCombinedRectangles(TSGrowableArray<RECTF>* combinedArray, uint32_t firstIndex, uint32_t lastIndex, const RECTF* rect) {
uint32_t index;
RECTF* checkRect;
for (index = firstIndex; index < lastIndex; index++) {
checkRect = &(*combinedArray)[index];
if (CheckForIntersection(rect, checkRect)) {
break;
}
}
if (index >= lastIndex) {
AddCombinedRect(combinedArray, rect);
return;
}
RECTF newrect[4];
newrect[0].left = rect->left;
newrect[0].bottom = rect->bottom;
newrect[0].right = rect->right;
newrect[0].top = checkRect->bottom;
newrect[1].left = rect->left;
newrect[1].bottom = checkRect->top;
newrect[1].right = rect->right;
newrect[1].top = rect->top;
newrect[2].left = rect->left;
newrect[2].bottom = std::max(checkRect->bottom, rect->bottom);
newrect[2].right = checkRect->left;
newrect[2].top = std::min(checkRect->top, rect->top);
newrect[3].left = checkRect->right;
newrect[3].bottom = std::max(checkRect->bottom, rect->bottom);
newrect[3].right = rect->right;
newrect[3].top = std::min(checkRect->top, rect->top);
for (uint32_t i = 0; i < 4; i++) {
if (!IsNullRect(&newrect[i])) {
FragmentCombinedRectangles(combinedArray, index + 1, lastIndex, &newrect[i]);
}
}
}
void FragmentSourceRectangles(TSGrowableArray<SOURCE>* sourceArray, uint32_t firstIndex, uint32_t lastIndex, int32_t previousOverlap, const RECTF* rect, void* param, int32_t sequence) { void FragmentSourceRectangles(TSGrowableArray<SOURCE>* sourceArray, uint32_t firstIndex, uint32_t lastIndex, int32_t previousOverlap, const RECTF* rect, void* param, int32_t sequence) {
if (firstIndex >= lastIndex) { if (firstIndex >= lastIndex) {
AddSourceRect(sourceArray, rect, param, sequence, SF_ADDING | (previousOverlap ? SF_OVERLAPS : SF_NONE)); AddSourceRect(sourceArray, rect, param, sequence, SF_ADDING | (previousOverlap ? SF_OVERLAPS : SF_NONE));
@ -141,6 +266,38 @@ void ProcessBooleanOperation(TSGrowableArray<SOURCE>* sourceArray, int32_t combi
} }
} }
int SortRectCallback(const void* elem1, const void* elem2) {
RECTF* rct1 = (RECTF*)elem1;
RECTF* rct2 = (RECTF*)elem2;
double result = rct1->top == rct2->top ? rct1->left - rct2->left : rct1->top - rct2->top;
if (result > 0.0) return 1;
if (result < 0.0) return -1;
return 0;
}
void ProduceCombinedRectangles(RGN* rgn) {
rgn->combined.SetCount(0);
uint32_t sourcerects = rgn->source.Count();
SOURCE* sourcearray = rgn->source.Ptr();
for (uint32_t i = 0; i < sourcerects; i++) {
if (!(sourcearray[i].flags & SF_PARAMONLY)) {
FragmentCombinedRectangles(&rgn->combined, 0, rgn->combined.Count(), &sourcearray[i].rect);
}
}
CombineRectangles(&rgn->combined);
std::qsort(rgn->combined.Ptr(), rgn->combined.Count(), sizeof(rgn->combined.Ptr()[0]), SortRectCallback);
for (uint32_t i = rgn->combined.Count(); i > 0; i = rgn->combined.Count()) {
if (!IsNullRect(&rgn->combined[i-1])) break;
rgn->combined.SetCount(i - 1);
}
}
void SRgnCombineRectf(HSRGN handle, RECTF* rect, void* param, int32_t combineMode) { void SRgnCombineRectf(HSRGN handle, RECTF* rect, void* param, int32_t combineMode) {
STORM_VALIDATE_BEGIN; STORM_VALIDATE_BEGIN;
STORM_VALIDATE(handle); STORM_VALIDATE(handle);
@ -235,3 +392,32 @@ void SRgnGetBoundingRectf(HSRGN handle, RECTF* rect) {
rect->top = 0.0f; rect->top = 0.0f;
} }
} }
void SRgnGetRectsf(HSRGN handle, uint32_t* numrects, RECTF* buffer) {
STORM_VALIDATE_BEGIN;
STORM_VALIDATE(handle);
STORM_VALIDATE(numrects);
STORM_VALIDATE_END_VOID;
HLOCKEDRGN lockedHandle;
auto rgn = s_rgntable.Lock(handle, &lockedHandle, 0);
if (!rgn) {
*numrects = 0;
return;
}
if (rgn->dirty) {
ProduceCombinedRectangles(rgn);
rgn->dirty = 0;
}
if (buffer) {
*numrects = std::min(*numrects, rgn->combined.Count());
memcpy(buffer, rgn->combined.Ptr(), sizeof(rgn->combined.Ptr()[0]) * *numrects);
}
else {
*numrects = rgn->combined.Count();
}
s_rgntable.Unlock(lockedHandle);
}

View file

@ -12,4 +12,6 @@ void SRgnDelete(HSRGN handle);
void SRgnGetBoundingRectf(HSRGN handle, RECTF* rect); void SRgnGetBoundingRectf(HSRGN handle, RECTF* rect);
void SRgnGetRectsf(HSRGN handle, uint32_t* numrects, RECTF* buffer);
#endif #endif

View file

@ -94,3 +94,72 @@ TEST_CASE("SRgnCombineRectf", "[region]") {
SRgnDelete(region); SRgnDelete(region);
} }
} }
TEST_CASE("SRgnGetRectsf", "[region]") {
SECTION("retrieves empty list if nothing put in") {
HSRGN rgn;
SRgnCreate(&rgn, 0);
uint32_t numrects = 1;
RECTF buffer[1];
SRgnGetRectsf(rgn, &numrects, buffer);
CHECK(numrects == 0);
SRgnDelete(rgn);
}
SECTION("retrieves all rects that were put in") {
HSRGN rgn;
SRgnCreate(&rgn, 0);
RECTF rct1 = { 5, 5, 10, 10 };
RECTF rct2 = { 0, 0, 1, 1 };
SRgnCombineRectf(rgn, &rct1, nullptr, SRGN_OR);
SRgnCombineRectf(rgn, &rct2, nullptr, SRGN_OR);
uint32_t numrects = 0;
RECTF buffer[2];
SRgnGetRectsf(rgn, &numrects, nullptr);
REQUIRE(numrects == 2);
SRgnGetRectsf(rgn, &numrects, buffer);
CHECK(buffer[0].left == rct2.left);
CHECK(buffer[0].bottom == rct2.bottom);
CHECK(buffer[0].right == rct2.right);
CHECK(buffer[0].top == rct2.top);
CHECK(buffer[1].left == rct1.left);
CHECK(buffer[1].bottom == rct1.bottom);
CHECK(buffer[1].right == rct1.right);
CHECK(buffer[1].top == rct1.top);
SRgnDelete(rgn);
}
SECTION("automatically merges overlapping rects") {
HSRGN rgn;
SRgnCreate(&rgn, 0);
RECTF rct1 = { -10, -10, 10, 10 };
RECTF rct2 = { 0, 0, 1, 1 };
SRgnCombineRectf(rgn, &rct1, nullptr, SRGN_OR);
SRgnCombineRectf(rgn, &rct2, nullptr, SRGN_OR);
uint32_t numrects = 0;
RECTF buffer[1];
SRgnGetRectsf(rgn, &numrects, nullptr);
REQUIRE(numrects == 1);
SRgnGetRectsf(rgn, &numrects, buffer);
CHECK(buffer[0].left == -10);
CHECK(buffer[0].bottom == -10);
CHECK(buffer[0].right == 10);
CHECK(buffer[0].top == 10);
SRgnDelete(rgn);
}
}