diff options
-rw-r--r-- | src/core/SkRegion_rects.cpp | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/src/core/SkRegion_rects.cpp b/src/core/SkRegion_rects.cpp new file mode 100644 index 0000000000..0e77b1a9df --- /dev/null +++ b/src/core/SkRegion_rects.cpp @@ -0,0 +1,283 @@ +#include "SkRegion.h" +#include "SkChunkAlloc.h" +#include "SkTDArray.h" +#include "SkTemplates.h" + +#if 0 + +struct VEdge { + VEdge* fPrev; + VEdge* fNext; + + SkRegion::RunType fX; + SkRegion::RunType fTop; + SkRegion::RunType fBottom; + int fWinding; + + void removeFromList() { + fPrev->fNext = fNext; + fNext->fPrev = fPrev; + } + + void backwardsInsert() { + while (fPrev->fX > fX) { + VEdge* prev = fPrev; + VEdge* next = this; + + // remove prev from the list + prev->fPrev->fNext = next; + next->fPrev = prev->fPrev; + + // insert prev after next + prev->fNext = next->fNext; + next->fNext->fPrev = prev; + next->fNext = prev; + prev->fPrev = next; + } + } + + static void SetFromRect(VEdge edges[], const SkIRect& r) { + edges[0].fX = r.fLeft; + edges[0].fTop = r.fTop; + edges[0].fBottom = r.fBottom; + edges[0].fWinding = -1; + + edges[1].fX = r.fRight; + edges[1].fTop = r.fTop; + edges[1].fBottom = r.fBottom; + edges[1].fWinding = 1; + } +}; + +class Accumulator { +public: + Accumulator(SkRegion::RunType top, int numRects); + ~Accumulator() {} + + SkRegion::RunType append(SkRegion::RunType top, const VEdge* edge); + + int count() const { return fTotalCount; } + void copyTo(SkRegion::RunType dst[]); + +private: + struct Row { + SkRegion::RunType* fPtr; + SkRegion::RunType fBottom; + int fCount; // just [L R] count + }; + SkChunkAlloc fAlloc; + SkTDArray<Row> fRows; + SkRegion::RunType fTop; + int fTotalCount; + int fRectCount; +}; + +Accumulator::Accumulator(SkRegion::RunType top, int numRects) + : fAlloc((1 + numRects * 2 + 1) * sizeof(int32_t)) { + fRectCount = numRects; + fTop = top; + fTotalCount = 2; // Top + final sentinel +} + +//#define TRACE_ROW(code) code +#define TRACE_ROW(code) + +SkRegion::RunType Accumulator::append(SkRegion::RunType currY, const VEdge* edge) { + // worst-case size + size_t size = fRectCount * 2 * sizeof(SkRegion::RunType); + SkRegion::RunType* row = (SkRegion::RunType*)fAlloc.allocThrow(size); + SkRegion::RunType* rowHead = row; + + SkRegion::RunType nextY = SkRegion::kRunTypeSentinel; + int winding = edge->fWinding; + + // record the L R values for this row + + if (edge->fTop > currY) { + nextY = SkMin32(nextY, edge->fTop); + TRACE_ROW(SkDebugf("Y %d\n", currY);) + } else { + SkRegion::RunType currR; + *row++ = edge->fX; + TRACE_ROW(SkDebugf("Y %d [%d", currY, edge->fX);) + edge = edge->fNext; + for (;;) { + if (edge->fTop > currY) { + nextY = SkMin32(nextY, edge->fTop); + break; + } + + int prevWinding = winding; + winding += edge->fWinding; + if (0 == winding) { // we finished an interval + currR = edge->fX; + } else if (0 == prevWinding && edge->fX > currR) { + *row++ = currR; + *row++ = edge->fX; + TRACE_ROW(SkDebugf(" %d] [%d", currR, edge->fX);) + } + + nextY = SkMin32(nextY, edge->fBottom); + edge = edge->fNext; + } + SkASSERT(0 == winding); + *row++ = currR; + TRACE_ROW(SkDebugf(" %d]\n", currR);) + } + int rowCount = row - rowHead; + + // now see if we have already seen this row, or if its unique + + Row* r = fRows.count() ? &fRows[fRows.count() - 1] : NULL; + if (r && (r->fCount == rowCount) && + !memcmp(r->fPtr, rowHead, + rowCount * sizeof(SkRegion::RunType))) { + r->fBottom = nextY; // update bottom + fAlloc.unalloc(rowHead); + } else { + Row* r = fRows.append(); + r->fPtr = rowHead; + r->fBottom = nextY; + r->fCount = rowCount; + fTotalCount += 1 + rowCount + 1; + } + + return nextY; +} + +void Accumulator::copyTo(SkRegion::RunType dst[]) { + SkDEBUGCODE(SkRegion::RunType* startDst = dst;) + + *dst++ = fTop; + + const Row* curr = fRows.begin(); + const Row* stop = fRows.end(); + while (curr < stop) { + *dst++ = curr->fBottom; + memcpy(dst, curr->fPtr, curr->fCount * sizeof(SkRegion::RunType)); + dst += curr->fCount; + *dst++ = SkRegion::kRunTypeSentinel; + curr += 1; + } + *dst++ = SkRegion::kRunTypeSentinel; + SkASSERT(dst - startDst == fTotalCount); +} + +/////////////////////////////////////////////////////////////////////////////// + +template <typename T> int SkTCmp2Int(const T& a, const T& b) { + return (a < b) ? -1 : ((b < a) ? 1 : 0); +} + +static inline int SkCmp32(int32_t a, int32_t b) { + return (a < b) ? -1 : ((b < a) ? 1 : 0); +} + +static int compare_edgeptr(const void* p0, const void* p1) { + const VEdge* e0 = *static_cast<VEdge*const*>(p0); + const VEdge* e1 = *static_cast<VEdge*const*>(p1); + + SkRegion::RunType v0 = e0->fTop; + SkRegion::RunType v1 = e1->fTop; + + if (v0 == v1) { + v0 = e0->fX; + v1 = e1->fX; + } + return SkCmp32(v0, v1); +} + +// fillout edge[] from rects[], sorted. Return the head, and set the tail +// +static VEdge* sort_edges(VEdge** edgePtr, VEdge edge[], const SkIRect rects[], + int rectCount, VEdge** edgeTail) { + int i; + VEdge** ptr = edgePtr; + for (int i = 0; i < rectCount; i++) { + if (!rects[i].isEmpty()) { + VEdge::SetFromRect(edge, rects[i]); + *ptr++ = edge++; + *ptr++ = edge++; + } + } + + int edgeCount = ptr - edgePtr; + if (0 == edgeCount) { + // all the rects[] were empty + return NULL; + } + + qsort(edgePtr, edgeCount, sizeof(*edgePtr), compare_edgeptr); + for (i = 1; i < edgeCount; i++) { + edgePtr[i - 1]->fNext = edgePtr[i]; + edgePtr[i]->fPrev = edgePtr[i - 1]; + } + *edgeTail = edgePtr[edgeCount - 1]; + return edgePtr[0]; +} + +bool SkRegion::setRects(const SkIRect rects[], int rectCount) { + if (0 == rectCount) { + return this->setEmpty(); + } + if (1 == rectCount) { + return this->setRect(rects[0]); + } + + int edgeCount = rectCount * 2; + SkAutoMalloc memory((sizeof(VEdge) + sizeof(VEdge*)) * edgeCount); + VEdge** edgePtr = (VEdge**)memory.get(); + VEdge* tail, *head = (VEdge*)(edgePtr + edgeCount); + head = sort_edges(edgePtr, head, rects, rectCount, &tail); + // check if we have no edges + if (NULL == head) { + return this->setEmpty(); + } + + // at this stage, we don't really care about edgeCount, or if rectCount is + // larger that it should be (since sort_edges might have skipped some + // empty rects[]). rectCount now is just used for worst-case allocations + + VEdge headEdge, tailEdge; + headEdge.fPrev = NULL; + headEdge.fNext = head; + headEdge.fTop = SK_MinS32; + headEdge.fX = SK_MinS32; + head->fPrev = &headEdge; + + tailEdge.fPrev = tail; + tailEdge.fNext = NULL; + tailEdge.fTop = SK_MaxS32; + tail->fNext = &tailEdge; + + int32_t currY = head->fTop; + Accumulator accum(currY, rectCount); + + while (head->fNext) { + VEdge* edge = head; + // accumulate the current + SkRegion::RunType nextY = accum.append(currY, edge); + // remove the old + while (edge->fTop <= currY) { + VEdge* next = edge->fNext; + if (edge->fBottom <= nextY) { + edge->removeFromList(); + } + edge = next; + } + // insert (sorted) the new + while (edge->fTop == nextY) { + VEdge* next = edge->fNext; + edge->backwardsInsert(); + edge = next; + } + currY = nextY; + head = headEdge.fNext; + } + + SkAutoTArray<RunType> runs(accum.count()); + accum.copyTo(runs.get()); + return this->setRuns(runs.get(), accum.count()); +} + +#endif |