/* * Copyright 2011 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #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