#include "storm/Region.hpp" #include "storm/region/RGN.hpp" #include "storm/Error.hpp" #include "storm/Hash.hpp" #include "storm/Thread.hpp" #include static TSExportTableSyncReuse s_rgntable; void AddSourceRect(TSGrowableArray* sourceArray, const RECTF* rect, void* param, int32_t sequence, uint32_t flags) { auto source = sourceArray->New(); source->rect = *rect; source->param = param; source->sequence = sequence; source->flags = flags; } int32_t CheckForIntersection(const RECTF* sourceRect, const RECTF* targetRect) { return sourceRect->left < targetRect->right && sourceRect->bottom < targetRect->top && sourceRect->right > targetRect->left && sourceRect->top > targetRect->bottom; } int32_t CompareRects(const RECTF* rect1, const RECTF* rect2) { return rect1->left == rect2->left && rect1->bottom == rect2->bottom && rect1->right == rect2->right && rect1->top == rect2->top; } void DeleteRect(RECTF* rect) { rect->left = std::numeric_limits::max(); rect->bottom = std::numeric_limits::max(); rect->right = std::numeric_limits::max(); rect->top = std::numeric_limits::max(); } void FragmentSourceRectangles(TSGrowableArray* sourceArray, uint32_t firstIndex, uint32_t lastIndex, int32_t previousOverlap, const RECTF* rect, void* param, int32_t sequence) { if (firstIndex >= lastIndex) { AddSourceRect(sourceArray, rect, param, sequence, SF_ADDING | (previousOverlap ? SF_OVERLAPS : SF_NONE)); return; } auto overlapsExisting = previousOverlap; for (uint32_t i = firstIndex; i < lastIndex; i++) { auto source = &(*sourceArray)[i]; if (CheckForIntersection(rect, &source->rect)) { if (!CompareRects(rect, &source->rect)) { break; } source->flags |= SF_OVERLAPS; overlapsExisting = 1; } if (i + 1 == lastIndex) { AddSourceRect(sourceArray, rect, param, sequence, SF_ADDING | (previousOverlap ? SF_OVERLAPS : SF_NONE)); return; } } // TODO } int32_t IsNullRect(RECTF* rect) { return rect->left >= rect->right || rect->bottom >= rect->top; } void ClearRegion(RGN* rgn) { rgn->source.SetCount(0); rgn->combined.SetCount(0); rgn->foundparams.SetCount(0); DeleteRect(&rgn->foundparamsrect); rgn->sequence = 0; rgn->dirty = 0; } void InvalidateRegion(RGN* rgn) { rgn->dirty = 1; DeleteRect(&rgn->foundparamsrect); } void DeleteSourceRect(TSGrowableArray* sourceArray, uint32_t index) { auto source = &(*sourceArray)[index]; DeleteRect(&source->rect); source->param = nullptr; source->sequence = -1; source->flags = SF_NONE; } void OptimizeSource(TSGrowableArray* sourceArray) { for (uint32_t i = 0; i < sourceArray->Count(); i++) { auto source = &(*sourceArray)[i]; if (IsNullRect(&source->rect)) { // Set current (null) element to last element auto last = &(*sourceArray)[sourceArray->Count() - 1]; (*sourceArray)[i] = *last; // Decrement index by 1 to force null check on copied last element on next tick i--; // Shrink by 1 (to account for the removal of the null element) sourceArray->SetCount(sourceArray->Count() - 1); } } } void ProcessBooleanOperation(TSGrowableArray* sourceArray, int32_t combineMode) { for (uint32_t i = 0; i < sourceArray->Count(); i++) { auto source = &(*sourceArray)[i]; int32_t remove = 0; switch (combineMode) { case SRGN_AND: remove = !(source->flags & SF_OVERLAPS); break; case SRGN_XOR: remove = source->flags & SF_OVERLAPS; break; case SRGN_DIFF: remove = source->flags & (SF_ADDING | SF_OVERLAPS); break; case SRGN_COPY: remove = source->flags & SF_ADDING; break; } if (remove) { DeleteSourceRect(sourceArray, i); } source->flags = SF_NONE; } } void SRgnCombineRectf(HSRGN handle, RECTF* rect, void* param, int32_t combineMode) { STORM_VALIDATE_BEGIN; STORM_VALIDATE(handle); STORM_VALIDATE(rect); STORM_VALIDATE(combineMode >= 1); STORM_VALIDATE(combineMode <= 6); STORM_VALIDATE_END_VOID; HLOCKEDRGN lockedHandle; auto rgn = s_rgntable.Lock(handle, &lockedHandle, 0); if (rgn) { if (combineMode == SRGN_OR || combineMode == SRGN_PARAMONLY) { if (!IsNullRect(rect)) { rgn->sequence++; AddSourceRect(&rgn->source, rect, param, rgn->sequence, combineMode == SRGN_PARAMONLY ? SF_PARAMONLY : SF_NONE); } } else { if (!IsNullRect(rect)) { rgn->sequence++; FragmentSourceRectangles(&rgn->source, 0, rgn->source.Count(), 0, rect, param, rgn->sequence); } ProcessBooleanOperation(&rgn->source, combineMode); OptimizeSource(&rgn->source); } InvalidateRegion(rgn); } s_rgntable.Unlock(lockedHandle); } void SRgnCreate(HSRGN* handlePtr, uint32_t reserved) { STORM_VALIDATE_BEGIN; STORM_VALIDATE(handlePtr); STORM_VALIDATE(!reserved); STORM_VALIDATE_END_VOID; HLOCKEDRGN lockedHandle = nullptr; auto rgn = s_rgntable.NewLock(handlePtr, &lockedHandle); ClearRegion(rgn); s_rgntable.Unlock(lockedHandle); } void SRgnDelete(HSRGN handle) { STORM_VALIDATE_BEGIN; STORM_VALIDATE(handle); STORM_VALIDATE_END_VOID; s_rgntable.Delete(handle); } void SRgnGetBoundingRectf(HSRGN handle, RECTF* rect) { STORM_VALIDATE_BEGIN; STORM_VALIDATE(handle); STORM_VALIDATE(rect); STORM_VALIDATE_END_VOID; rect->left = std::numeric_limits::max(); rect->bottom = std::numeric_limits::max(); rect->right = std::numeric_limits::min(); rect->top = std::numeric_limits::min(); HLOCKEDRGN lockedHandle; auto rgn = s_rgntable.Lock(handle, &lockedHandle, 0); if (!rgn) { s_rgntable.Unlock(lockedHandle); return; } for (uint32_t i = 0; i < rgn->source.Count(); i++) { auto source = &rgn->source[i]; if (!(source->flags & SF_PARAMONLY)) { rect->left = std::min(source->rect.left, rect->left); rect->bottom = std::min(source->rect.bottom, rect->bottom); rect->right = std::max(source->rect.right, rect->right); rect->top = std::max(source->rect.top, rect->top); } } s_rgntable.Unlock(lockedHandle); if (IsNullRect(rect)) { rect->left = 0.0f; rect->bottom = 0.0f; rect->right = 0.0f; rect->top = 0.0f; } }