| /* |
| * 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 "SkAntiEdge.h" |
| #include "SkPoint.h" |
| |
| /** Returns the signed fraction of a SkFixed |
| */ |
| static inline SkFixed SkFixedFraction(SkFixed x) |
| { |
| SkFixed mask = x >> 31 << 16; |
| return (x & 0xFFFF) | mask; |
| } |
| |
| void SkAntiEdge::pointOnLine(SkFixed x, SkFixed y) { |
| float x0 = SkFixedToFloat(x); |
| float y0 = SkFixedToFloat(y); |
| float x1 = SkFixedToFloat(fFirstX); |
| float y1 = SkFixedToFloat(fFirstY); |
| float x2 = SkFixedToFloat(fLastX); |
| float y2 = SkFixedToFloat(fLastY); |
| float numer = (x2 - x1) * (y1 - y0) - (x1 - x0) * (y2 - y1); |
| float denom = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1); |
| double dist = fabs(numer) / sqrt(denom); |
| SkAssertResult(dist < 0.01); |
| } |
| |
| void SkAntiEdge::pointInLine(SkFixed x, SkFixed y) { |
| if (y == SK_MaxS32) { |
| return; |
| } |
| pointOnLine(x, y); |
| SkAssertResult(y >= fFirstY && y <= fLastY); |
| } |
| |
| void SkAntiEdge::validate() { |
| pointOnLine(fWalkX, fY); |
| pointOnLine(fX, fWalkY); |
| } |
| |
| bool SkAntiEdge::setLine(const SkPoint& p0, const SkPoint& p1) { |
| fFirstY = SkScalarToFixed(p0.fY); |
| fLastY = SkScalarToFixed(p1.fY); |
| if (fFirstY == fLastY) { |
| return false; |
| } |
| fFirstX = SkScalarToFixed(p0.fX); |
| fLastX = SkScalarToFixed(p1.fX); |
| if (fFirstY > fLastY) { |
| SkTSwap(fFirstX, fLastX); |
| SkTSwap(fFirstY, fLastY); |
| fWinding = -1; |
| } else { |
| fWinding = 1; |
| } |
| SkFixed dx = fLastX - fFirstX; |
| fDXFlipped = dx < 0; |
| SkFixed dy = fLastY - fFirstY; |
| fDX = SkFixedDiv(dx, dy); |
| fDY = dx == 0 ? SK_MaxS32 : SkFixedDiv(dy, SkFixedAbs(dx)); |
| fLink = NULL; |
| fLinkSet = false; |
| return true; |
| } |
| |
| void SkAntiEdge::calcLine() { |
| SkFixed yStartFrac = SkFixedFraction(fFirstY); |
| if (fDXFlipped) { |
| SkFixed vert = SK_Fixed1 - yStartFrac; // distance from y start to x-axis |
| fX0 = fFirstX + SkFixedMul(fDX, vert); |
| SkFixed backupX = fFirstX + SkFixedMul(vert, fDX); // x cell to back up to |
| SkFixed cellX = SkIntToFixed(SkFixedFloor(backupX)); |
| SkFixed endX = SkIntToFixed(SkFixedFloor(fLastX)); |
| if (cellX < endX) { |
| cellX = endX; |
| } |
| SkFixed distX = fFirstX - cellX; // to y-axis |
| fY0 = fFirstY + SkFixedMul(fDY, distX); |
| SkFixed rowBottom = SkIntToFixed(SkFixedCeil(fFirstY + 1)); |
| if (fLastY > rowBottom) { |
| fPartialY = 0; |
| fX = fX0; |
| fY = rowBottom; |
| } else { |
| fPartialY = SkFixedFraction(fLastY); |
| fX = fLastX; |
| fY = fLastY; |
| } |
| } else { |
| fPartialY = yStartFrac; |
| fX0 = fFirstX - SkFixedMul(fDX, yStartFrac); |
| fY0 = fFirstY; |
| if (fDY != SK_MaxS32) { |
| SkFixed xStartFrac = SkFixedFraction(fFirstX); |
| fY0 -= SkFixedMul(fDY, xStartFrac); |
| } |
| fX = fFirstX; |
| fY = fFirstY; |
| } |
| fWalkX = fX; |
| fWalkY = fY; |
| fFinished = false; |
| } |
| |
| static SkFixed SkFixedAddPin(SkFixed a, SkFixed b) { |
| SkFixed result = a + b; |
| if (((a ^ ~b) & (a ^ result)) >= 0) { // one positive, one negative |
| return result; // or all three same sign |
| } |
| return a < 0 ? -SK_FixedMax : SK_FixedMax; |
| } |
| |
| // edge is increasing in x and y |
| uint16_t SkAntiEdge::advanceX(SkFixed left) { |
| validate(); |
| SkFixed x = SkFixedAddPin(fX0, fDX); |
| SkFixed wy = SkIntToFixed(SkFixedFloor(fWalkY + SK_Fixed1)); |
| pointOnLine(x, wy); |
| SkFixed partial = SK_Fixed1 - fPartialY; |
| SkFixed bottomPartial = wy - fLastY; |
| if (bottomPartial > 0) { |
| partial -= bottomPartial; |
| } |
| if (x > fLastX) { |
| x = fLastX; |
| wy = fLastY; |
| } |
| uint16_t coverage; |
| if (left >= x) { |
| fFinished = true; |
| coverage = partial - 1; // walker is to the right of edge |
| } else { |
| SkFixed y = SkFixedAddPin(fY0, fDY); |
| SkFixed wx = SkIntToFixed(SkFixedFloor(fWalkX + SK_Fixed1)); |
| if (fDY != SK_MaxS32) { |
| pointOnLine(wx, y); |
| } |
| if (y > fLastY) { |
| y = fLastY; |
| wx = fLastX; |
| } |
| bool topCorner = fWalkX <= fX; |
| bool bottomCorner = x <= wx; |
| bool halfPlane = !(topCorner ^ bottomCorner); |
| if (halfPlane) { |
| if (x - SkIntToFixed(SkFixedFloor(fX)) <= SK_Fixed1) { |
| coverage = ~((fX + x) >> 1); // avg of fx, fx+dx |
| fFinished = true; |
| if (x >= left + SK_Fixed1) { |
| fWalkX = wx; |
| fY = fY0 = y; |
| } |
| } else { |
| SkAssertResult(y - SkIntToFixed(SkFixedFloor(fY)) <= SK_Fixed1); |
| coverage = ((fY + y) >> 1); |
| fFinished = y == fLastY; |
| fWalkX = wx; |
| fY = fY0 = y; |
| } |
| coverage = coverage * partial >> 16; |
| } else if (topCorner) { |
| SkFixed xDiff = wx - fX; |
| SkAssertResult(xDiff >= 0); |
| SkAssertResult(xDiff <= SK_Fixed1); |
| SkFixed yDiff = y - fWalkY; |
| // This may be a very small negative number if error accumulates |
| // FIXME: for now, try setting it to zero in that case. |
| if (yDiff < 0) { |
| fX = fX0 = SkIntToFixed(SkFixedCeil(fX)); |
| yDiff = 0; |
| } |
| SkAssertResult(yDiff >= 0); |
| SkAssertResult(yDiff <= SK_Fixed1); |
| int xCoverage = xDiff >> 1; // throw away 1 bit so multiply |
| int yCoverage = yDiff >> 1; // stays in range |
| int triangle = xCoverage * yCoverage; // 30 bits |
| SkFixed bottomPartial = y - fLastY; |
| fFinished = bottomPartial >= 0; |
| if (fFinished) { |
| yCoverage = bottomPartial >> 1; |
| xCoverage = (wx - fLastX) >> 1; |
| triangle -= xCoverage * yCoverage; |
| } |
| coverage = triangle >> 15; |
| fWalkX = wx; |
| fY = fY0 = y; |
| } else { |
| SkAssertResult(bottomCorner); |
| SkFixed xDiff = x - fWalkX; |
| SkAssertResult(xDiff >= 0); |
| SkAssertResult(xDiff <= SK_Fixed1); |
| SkFixed yDiff = wy - fY; |
| SkAssertResult(yDiff >= 0); |
| SkAssertResult(yDiff <= SK_Fixed1); |
| int xCoverage = xDiff >> 1; // throw away 1 bit so multiply |
| int yCoverage = yDiff >> 1; // stays in range |
| int triangle = xCoverage * yCoverage >> 15; |
| coverage = partial - 1 - triangle; |
| fFinished = true; |
| } |
| } |
| validate(); |
| return coverage; |
| } |
| |
| // edge is increasing in x, but decreasing in y |
| uint16_t SkAntiEdge::advanceFlippedX(SkFixed left) { |
| validate(); |
| SkFixed x = SkFixedAddPin(fX0, -fDX); |
| SkFixed wy = SkIntToFixed(SkFixedFloor(fWalkY - 1)); |
| pointOnLine(x, wy); |
| SkFixed partial = fPartialY ? fPartialY : SK_Fixed1; |
| SkFixed topPartial = fFirstY - wy; |
| if (topPartial > 0) { |
| partial -= topPartial; |
| } |
| if (x > fFirstX) { |
| x = fFirstX; |
| wy = fFirstY; |
| } |
| uint16_t coverage; |
| if (left >= x) { |
| fFinished = true; |
| coverage = partial - 1; // walker is to the right of edge |
| } else { |
| SkFixed y = SkFixedAddPin(fY0, -fDY); |
| SkFixed wx = SkIntToFixed(SkFixedFloor(fWalkX + SK_Fixed1)); |
| pointOnLine(wx, y); |
| if (y < fFirstY) { |
| y = fFirstY; |
| wx = fFirstX; |
| } |
| bool bottomCorner = fWalkX <= fX; |
| bool topCorner = x <= wx; |
| bool halfPlane = !(topCorner ^ bottomCorner); |
| if (halfPlane) { |
| if (x - SkIntToFixed(SkFixedFloor(fX)) <= SK_Fixed1) { |
| coverage = ~((fX + x) >> 1); // avg of fx, fx+dx |
| fFinished = true; |
| } else { |
| SkAssertResult(y - SkIntToFixed(SkFixedFloor(fY)) <= SK_Fixed1); |
| coverage = ~((fY + y) >> 1); |
| fFinished = y == fY; |
| fWalkX = wx; |
| fY = fY0 = y; |
| } |
| coverage = coverage * partial >> 16; |
| } else if (bottomCorner) { |
| SkFixed xDiff = wx - fX; |
| SkAssertResult(xDiff >= 0); |
| SkAssertResult(xDiff <= SK_Fixed1); |
| SkFixed yDiff = fWalkY - y; |
| SkAssertResult(yDiff >= 0); |
| SkAssertResult(yDiff <= SK_Fixed1); |
| int xCoverage = xDiff >> 1; // throw away 1 bit so multiply |
| int yCoverage = yDiff >> 1; // stays in range |
| int triangle = xCoverage * yCoverage; // 30 bits |
| SkFixed bottomPartial = fFirstY - y; |
| fFinished = bottomPartial >= 0; |
| if (fFinished) { |
| yCoverage = bottomPartial >> 1; |
| xCoverage = (wx - fFirstX) >> 1; |
| triangle -= xCoverage * yCoverage; |
| } |
| coverage = triangle >> 15; |
| fWalkX = wx; |
| fY = fY0 = y; |
| } else { |
| SkAssertResult(topCorner); |
| SkFixed xDiff = x - fWalkX; |
| SkAssertResult(xDiff >= 0); |
| SkAssertResult(xDiff <= SK_Fixed1); |
| SkFixed yDiff = fY - wy; |
| SkAssertResult(yDiff >= 0); |
| SkAssertResult(yDiff <= SK_Fixed1); |
| int xCoverage = xDiff >> 1; // throw away 1 bit so multiply |
| int yCoverage = yDiff >> 1; // stays in range |
| int triangle = xCoverage * yCoverage >> 15; |
| coverage = partial - 1 - triangle; |
| fFinished = true; |
| } |
| } |
| validate(); |
| return coverage; |
| } |
| |
| void SkAntiEdge::advanceY(SkFixed top) { |
| validate(); |
| fX0 = SkFixedAddPin(fX0, fDX); |
| fPartialY = 0; |
| if (fDXFlipped) { |
| if (fX0 < fLastX) { |
| fWalkX = fX = fLastX; |
| } else { |
| fWalkX = fX = fX0; |
| } |
| SkFixed bottom = top + SK_Fixed1; |
| if (bottom > fLastY) { |
| bottom = fLastY; |
| } |
| SkFixed vert = bottom - fFirstY; // distance from y start to x-axis |
| SkFixed backupX = fFirstX + SkFixedMul(vert, fDX); // x cell to back up to |
| SkFixed distX = fFirstX - SkIntToFixed(SkFixedFloor(backupX)); // to y-axis |
| fY0 = fFirstY + SkFixedMul(fDY, distX); |
| |
| fY = top + SK_Fixed1; |
| if (fY > fLastY) { |
| fY = fLastY; |
| } |
| if (fLastY < top + SK_Fixed1) { |
| fPartialY = SkFixedFraction(fLastY); |
| } |
| } else { |
| if (fX0 > fLastX) { |
| fX0 = fLastX; |
| } |
| fX = fX0; |
| } |
| fWalkY = SkIntToFixed(SkFixedFloor(fWalkY + SK_Fixed1)); |
| if (fWalkY > fLastY) { |
| fWalkY = fLastY; |
| } |
| validate(); |
| fFinished = false; |
| } |
| |
| int SkAntiEdgeBuilder::build(const SkPoint pts[], int count) { |
| SkAntiEdge* edge = fEdges.append(); |
| for (int index = 0; index < count; ++index) { |
| if (edge->setLine(pts[index], pts[(index + 1) % count])) { |
| edge = fEdges.append(); |
| } |
| } |
| int result = fEdges.count(); |
| fEdges.setCount(--result); |
| if (result > 0) { |
| sk_bzero(&fHeadEdge, sizeof(fHeadEdge)); |
| sk_bzero(&fTailEdge, sizeof(fTailEdge)); |
| for (int index = 0; index < result; ++index) { |
| *fList.append() = &fEdges[index]; |
| } |
| } |
| return result; |
| } |
| |
| void SkAntiEdgeBuilder::calc() { |
| for (SkAntiEdge* active = fEdges.begin(); active != fEdges.end(); ++active) { |
| active->calcLine(); |
| } |
| // compute winding sum for edges |
| SkAntiEdge* first = fHeadEdge.fNext; |
| SkAntiEdge* active; |
| SkAntiEdge* listTop = first; |
| for (active = first; active != &fTailEdge; active = active->fNext) { |
| active->fWindingSum = active->fWinding; |
| while (listTop->fLastY < active->fFirstY) { |
| listTop = listTop->fNext; |
| } |
| for (SkAntiEdge* check = listTop; check->fFirstY <= active->fFirstY; check = check->fNext) { |
| if (check == active) { |
| continue; |
| } |
| if (check->fLastY <= active->fFirstY) { |
| continue; |
| } |
| if (check->fFirstX > active->fFirstX) { |
| continue; |
| } |
| if (check->fFirstX == active->fFirstX && check->fDX > active->fDX) { |
| continue; |
| } |
| active->fWindingSum += check->fWinding; |
| } |
| } |
| } |
| |
| extern "C" { |
| static int edge_compare(const void* a, const void* b) { |
| const SkAntiEdge* edgea = *(const SkAntiEdge**)a; |
| const SkAntiEdge* edgeb = *(const SkAntiEdge**)b; |
| |
| int valuea = edgea->fFirstY; |
| int valueb = edgeb->fFirstY; |
| |
| if (valuea == valueb) { |
| valuea = edgea->fFirstX; |
| valueb = edgeb->fFirstX; |
| } |
| |
| if (valuea == valueb) { |
| valuea = edgea->fDX; |
| valueb = edgeb->fDX; |
| } |
| |
| return valuea - valueb; |
| } |
| } |
| |
| void SkAntiEdgeBuilder::sort(SkTDArray<SkAntiEdge*>& listOfEdges) { |
| SkAntiEdge** list = listOfEdges.begin(); |
| int count = listOfEdges.count(); |
| qsort(list, count, sizeof(SkAntiEdge*), edge_compare); |
| |
| // link the edges in sorted order |
| for (int i = 1; i < count; i++) { |
| list[i - 1]->fNext = list[i]; |
| list[i]->fPrev = list[i - 1]; |
| } |
| } |
| |
| #define kEDGE_HEAD_XY SK_MinS32 |
| #define kEDGE_TAIL_XY SK_MaxS32 |
| |
| void SkAntiEdgeBuilder::sort() { |
| sort(fList); |
| SkAntiEdge* last = fList.end()[-1]; |
| fHeadEdge.fNext = fList[0]; |
| fHeadEdge.fFirstX = fHeadEdge.fFirstY = fHeadEdge.fWalkY = fHeadEdge.fLastY = kEDGE_HEAD_XY; |
| fList[0]->fPrev = &fHeadEdge; |
| |
| fTailEdge.fPrev = last; |
| fTailEdge.fFirstX = fTailEdge.fFirstY = fTailEdge.fWalkY = fTailEdge.fLastY = kEDGE_TAIL_XY; |
| last->fNext = &fTailEdge; |
| } |
| |
| static inline void remove_edge(SkAntiEdge* edge) { |
| edge->fPrev->fNext = edge->fNext; |
| edge->fNext->fPrev = edge->fPrev; |
| } |
| |
| static inline void swap_edges(SkAntiEdge* prev, SkAntiEdge* next) { |
| SkASSERT(prev->fNext == next && next->fPrev == prev); |
| |
| // 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 backward_insert_edge_based_on_x(SkAntiEdge* edge SkDECLAREPARAM(int, y)) { |
| SkFixed x = edge->fFirstX; |
| |
| for (;;) { |
| SkAntiEdge* prev = edge->fPrev; |
| |
| // add 1 to curr_y since we may have added new edges (built from curves) |
| // that start on the next scanline |
| SkASSERT(prev && SkFixedFloor(prev->fWalkY - prev->fDXFlipped) <= y + 1); |
| |
| if (prev->fFirstX <= x) { |
| break; |
| } |
| swap_edges(prev, edge); |
| } |
| } |
| |
| static void insert_new_edges(SkAntiEdge* newEdge, SkFixed curr_y) { |
| int y = SkFixedFloor(curr_y); |
| if (SkFixedFloor(newEdge->fWalkY - newEdge->fDXFlipped) < y) { |
| return; |
| } |
| while (SkFixedFloor(newEdge->fWalkY - newEdge->fDXFlipped) == y) { |
| SkAntiEdge* next = newEdge->fNext; |
| backward_insert_edge_based_on_x(newEdge SkPARAM(y)); |
| newEdge = next; |
| } |
| } |
| |
| static int find_active_edges(int y, SkAntiEdge** activeLeft, |
| SkAntiEdge** activeLast) { |
| SkAntiEdge* first = *activeLeft; |
| SkFixed bottom = first->fLastY; |
| SkAntiEdge* active = first->fNext; |
| first->fLinkSet = false; |
| SkFixed yLimit = SkIntToFixed(y + 1); // limiting pixel edge |
| for ( ; active->fWalkY != kEDGE_TAIL_XY; active = active->fNext) { |
| active->fLinkSet = false; |
| if (yLimit <= active->fWalkY - active->fDXFlipped) { |
| break; |
| } |
| if ((*activeLeft)->fWalkX > active->fWalkX) { |
| *activeLeft = active; |
| } |
| if (bottom > active->fLastY) { |
| bottom = active->fLastY; |
| } |
| } |
| *activeLast = active; |
| return SkFixedCeil(bottom); |
| } |
| |
| // All edges are oriented to increase in y. Link edges with common tops and |
| // bottoms so the links can share their winding sum. |
| void SkAntiEdgeBuilder::link() { |
| SkAntiEdge* tail = fEdges.end(); |
| // look for links forwards and backwards |
| SkAntiEdge* prev = fEdges.begin(); |
| SkAntiEdge* active; |
| for (active = prev + 1; active != tail; ++active) { |
| if (prev->fWinding == active->fWinding) { |
| if (prev->fLastX == active->fFirstX && prev->fLastY == active->fFirstY) { |
| prev->fLink = active; |
| active->fLinkSet = true; |
| } else if (active->fLastX == prev->fFirstX && active->fLastY == prev->fFirstY) { |
| active->fLink = prev; |
| prev->fLinkSet = true; |
| } |
| } |
| prev = active; |
| } |
| // look for stragglers |
| prev = fEdges.begin() - 1; |
| do { |
| do { |
| if (++prev == tail) { |
| return; |
| } |
| } while (prev->fLinkSet || NULL != prev->fLink); |
| for (active = prev + 1; active != tail; ++active) { |
| if (active->fLinkSet || NULL != active->fLink) { |
| continue; |
| } |
| if (prev->fWinding != active->fWinding) { |
| continue; |
| } |
| if (prev->fLastX == active->fFirstX && prev->fLastY == active->fFirstY) { |
| prev->fLink = active; |
| active->fLinkSet = true; |
| break; |
| } |
| if (active->fLastX == prev->fFirstX && active->fLastY == prev->fFirstY) { |
| active->fLink = prev; |
| prev->fLinkSet = true; |
| break; |
| } |
| } |
| } while (true); |
| } |
| |
| void SkAntiEdgeBuilder::split(SkAntiEdge* edge, SkFixed y) { |
| SkPoint upperPoint = {edge->fFirstX, edge->fFirstY}; |
| SkPoint midPoint = {edge->fFirstX + SkMulDiv(y - edge->fFirstY, |
| edge->fLastX - edge->fFirstX, edge->fLastY - edge->fFirstY), y}; |
| SkPoint lowerPoint = {edge->fLastX, edge->fLastY}; |
| int8_t winding = edge->fWinding; |
| edge->setLine(upperPoint, midPoint); |
| edge->fWinding = winding; |
| SkAntiEdge* lower = fEdges.append(); |
| lower->setLine(midPoint, lowerPoint); |
| lower->fWinding = winding; |
| insert_new_edges(lower, y); |
| } |
| |
| // An edge computes pixel coverage by considering the integral winding value |
| // to its left. If an edge is enclosed by fractional winding, split it. |
| // FIXME: This is also a good time to find crossing edges and split them, too. |
| void SkAntiEdgeBuilder::split() { |
| // create a new set of edges that describe the whole link |
| SkTDArray<SkAntiEdge> links; |
| SkAntiEdge* first = fHeadEdge.fNext; |
| SkAntiEdge* active; |
| for (active = first; active != &fTailEdge; active = active->fNext) { |
| if (active->fLinkSet || NULL == active->fLink) { |
| continue; |
| } |
| SkAntiEdge* link = links.append(); |
| link->fFirstX = active->fFirstX; |
| link->fFirstY = active->fFirstY; |
| SkAntiEdge* linkEnd; |
| SkAntiEdge* next = active; |
| do { |
| linkEnd = next; |
| next = next->fLink; |
| } while (NULL != next); |
| link->fLastX = linkEnd->fLastX; |
| link->fLastY = linkEnd->fLastY; |
| } |
| // create a list of all edges, links and singletons |
| SkTDArray<SkAntiEdge*> list; |
| for (active = links.begin(); active != links.end(); ++active) { |
| *list.append() = active; |
| } |
| for (active = first; active != &fTailEdge; active = active->fNext) { |
| if (!active->fLinkSet && NULL == active->fLink) { |
| SkAntiEdge* link = links.append(); |
| link->fFirstX = active->fFirstX; |
| link->fFirstY = active->fFirstY; |
| link->fLastX = active->fLastX; |
| link->fLastY = active->fLastY; |
| *list.append() = link; |
| } |
| } |
| SkAntiEdge tail; |
| tail.fFirstY = tail.fLastY = kEDGE_TAIL_XY; |
| *list.append() = &tail; |
| sort(list); |
| // walk the list, splitting edges partially occluded on the left |
| SkAntiEdge* listTop = list[0]; |
| for (active = first; active != &fTailEdge; active = active->fNext) { |
| while (listTop->fLastY < active->fFirstY) { |
| listTop = listTop->fNext; |
| } |
| for (SkAntiEdge* check = listTop; check->fFirstY < active->fLastY; check = check->fNext) { |
| if (check->fFirstX > active->fFirstX) { |
| continue; |
| } |
| if (check->fFirstX == active->fFirstX && check->fDX > active->fDX) { |
| continue; |
| } |
| if (check->fFirstY > active->fFirstY) { |
| split(active, check->fFirstY); |
| } |
| if (check->fLastY < active->fLastY) { |
| split(active, check->fLastY); |
| } |
| } |
| } |
| } |
| |
| static inline uint8_t coverage_to_8(int coverage) { |
| uint16_t x = coverage < 0 ? 0 : coverage > 0xFFFF ? 0xFFFF : coverage; |
| // for values 0x7FFF and smaller, add (0x7F - high byte) and trunc |
| // for values 0x8000 and larger, subtract (high byte - 0x80) and trunc |
| return (x + 0x7f + (x >> 15) - (x >> 8)) >> 8; |
| } |
| |
| void SkAntiEdgeBuilder::walk(uint8_t* result, int rowBytes, int height) { |
| SkAntiEdge* first = fHeadEdge.fNext; |
| SkFixed top = first->fWalkY - first->fDXFlipped; |
| int y = SkFixedFloor(top); |
| do { |
| SkAntiEdge* activeLeft = first; |
| SkAntiEdge* activeLast, * active; |
| int yLast = find_active_edges(y, &activeLeft, &activeLast); |
| while (y < yLast) { |
| SkAssertResult(y >= 0); |
| SkAssertResult(y < height); |
| SkFixed left = activeLeft->fWalkX; |
| int x = SkFixedFloor(left); |
| uint8_t* resultPtr = &result[y * rowBytes + x]; |
| bool finished; |
| do { |
| left = SkIntToFixed(x); |
| SkAssertResult(x >= 0); |
| // SkAssertResult(x < pixelCol); |
| if (x >= rowBytes) { // FIXME: cumulative error in fX += fDX |
| break; // fails to set fFinished early enough |
| } // see test 6 (dy<dx) |
| finished = true; |
| int coverage = 0; |
| for (active = first; active != activeLast; active = active->fNext) { |
| if (left + SK_Fixed1 <= active->fX) { |
| finished = false; |
| continue; // walker is to the left of edge |
| } |
| int cover = active->fDXFlipped ? |
| active->advanceFlippedX(left) : active->advanceX(left); |
| if (0 == active->fWindingSum) { |
| cover = -cover; |
| } |
| coverage += cover; |
| finished &= active->fFinished; |
| } |
| uint8_t old = *resultPtr; |
| uint8_t pix = coverage_to_8(coverage); |
| uint8_t blend = old > pix ? old : pix; |
| *resultPtr++ = blend; |
| ++x; |
| } while (!finished); |
| ++y; |
| top = SkIntToFixed(y); |
| SkFixed topLimit = top + SK_Fixed1; |
| SkFixed xSort = -SK_FixedMax; |
| for (active = first; active != activeLast; active = active->fNext) { |
| if (xSort > active->fX || topLimit > active->fLastY) { |
| yLast = y; // recompute bottom after all Ys are advanced |
| } |
| xSort = active->fX; |
| if (active->fWalkY < active->fLastY) { |
| active->advanceY(top); |
| } |
| } |
| for (active = first; active != activeLast; ) { |
| SkAntiEdge* next = active->fNext; |
| if (top >= active->fLastY) { |
| remove_edge(active); |
| } |
| active = next; |
| } |
| first = fHeadEdge.fNext; |
| } |
| SkAntiEdge* prev = activeLast->fPrev; |
| if (prev != &fHeadEdge) { |
| insert_new_edges(prev, top); |
| first = fHeadEdge.fNext; |
| } |
| } while (first->fWalkY < kEDGE_TAIL_XY); |
| } |
| |
| void SkAntiEdgeBuilder::process(const SkPoint* points, int ptCount, |
| uint8_t* result, int pixelCol, int pixelRow) { |
| if (ptCount < 3) { |
| return; |
| } |
| int count = build(points, ptCount); |
| if (count == 0) { |
| return; |
| } |
| SkAssertResult(count > 1); |
| link(); |
| sort(); |
| split(); |
| calc(); |
| walk(result, pixelCol, pixelRow); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| int test3by3_test; |
| |
| // input is a rectangle |
| static void test_3_by_3() { |
| const int pixelRow = 3; |
| const int pixelCol = 3; |
| const int ptCount = 4; |
| const int pixelCount = pixelRow * pixelCol; |
| const SkPoint tests[][ptCount] = { |
| {{2.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, 2.0f}, {2.0f, 2.0f}}, // 0: full rect |
| {{2.5f, 1.0f}, {1.5f, 1.0f}, {1.5f, 2.0f}, {2.5f, 2.0f}}, // 1: y edge |
| {{2.0f, 1.5f}, {1.0f, 1.5f}, {1.0f, 2.5f}, {2.0f, 2.5f}}, // 2: x edge |
| {{2.5f, 1.5f}, {1.5f, 1.5f}, {1.5f, 2.5f}, {2.5f, 2.5f}}, // 3: x/y edge |
| {{2.8f, 0.2f}, {0.2f, 0.2f}, {0.2f, 2.8f}, {2.8f, 2.8f}}, // 4: large |
| {{1.8f, 1.2f}, {1.2f, 1.2f}, {1.2f, 1.8f}, {1.8f, 1.8f}}, // 5: small |
| {{0.0f, 0.0f}, {0.0f, 1.0f}, {3.0f, 2.0f}, {3.0f, 1.0f}}, // 6: dy<dx |
| {{3.0f, 0.0f}, {0.0f, 1.0f}, {0.0f, 2.0f}, {3.0f, 1.0f}}, // 7: dy<-dx |
| {{1.0f, 0.0f}, {0.0f, 0.0f}, {1.0f, 3.0f}, {2.0f, 3.0f}}, // 8: dy>dx |
| {{2.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 3.0f}, {1.0f, 3.0f}}, // 9: dy>-dx |
| {{0.5f, 0.5f}, {0.5f, 1.5f}, {2.5f, 2.5f}, {2.5f, 1.5f}}, // 10: dy<dx 2 |
| {{2.5f, 0.5f}, {0.5f, 1.5f}, {0.5f, 2.5f}, {2.5f, 1.5f}}, // 11: dy<-dx 2 |
| {{0.0f, 0.0f}, {2.0f, 0.0f}, {2.0f, 2.0f}, {0.0f, 2.0f}}, // 12: 2x2 |
| {{0.0f, 0.0f}, {3.0f, 0.0f}, {3.0f, 3.0f}, {0.0f, 3.0f}}, // 13: 3x3 |
| {{1.75f, 0.25f}, {2.75f, 1.25f}, {1.25f, 2.75f}, {0.25f, 1.75f}}, // 14 |
| {{2.25f, 0.25f}, {2.75f, 0.75f}, {0.75f, 2.75f}, {0.25f, 2.25f}}, // 15 |
| {{0.25f, 0.75f}, {0.75f, 0.25f}, {2.75f, 2.25f}, {2.25f, 2.75f}}, // 16 |
| {{1.25f, 0.50f}, {1.75f, 0.25f}, {2.75f, 2.25f}, {2.25f, 2.50f}}, // 17 |
| {{1.00f, 0.75f}, {2.00f, 0.50f}, {2.00f, 1.50f}, {1.00f, 1.75f}}, // 18 |
| {{1.00f, 0.50f}, {2.00f, 0.75f}, {2.00f, 1.75f}, {1.00f, 1.50f}}, // 19 |
| {{1.00f, 0.75f}, {1.00f, 1.75f}, {2.00f, 1.50f}, {2.00f, 0.50f}}, // 20 |
| {{1.00f, 0.50f}, {1.00f, 1.50f}, {2.00f, 1.75f}, {2.00f, 0.75f}}, // 21 |
| }; |
| const uint8_t results[][pixelCount] = { |
| {0x00, 0x00, 0x00, // 0: 1 pixel rect |
| 0x00, 0xFF, 0x00, |
| 0x00, 0x00, 0x00}, |
| {0x00, 0x00, 0x00, // 1: y edge |
| 0x00, 0x7F, 0x80, |
| 0x00, 0x00, 0x00}, |
| {0x00, 0x00, 0x00, // 2: x edge |
| 0x00, 0x7F, 0x00, |
| 0x00, 0x7F, 0x00}, |
| {0x00, 0x00, 0x00, // 3: x/y edge |
| 0x00, 0x40, 0x40, |
| 0x00, 0x40, 0x40}, |
| {0xA3, 0xCC, 0xA3, // 4: large |
| 0xCC, 0xFF, 0xCC, |
| 0xA3, 0xCC, 0xA3}, |
| {0x00, 0x00, 0x00, // 5: small |
| 0x00, 0x5C, 0x00, |
| 0x00, 0x00, 0x00}, |
| {0xD5, 0x80, 0x2B, // 6: dy<dx |
| 0x2A, 0x7F, 0xD4, |
| 0x00, 0x00, 0x00}, |
| {0x2B, 0x80, 0xD5, // 7: dy<-dx |
| 0xD4, 0x7F, 0x2A, |
| 0x00, 0x00, 0x00}, |
| {0xD5, 0x2A, 0x00, // 8: dy>dx |
| 0x80, 0x7F, 0x00, |
| 0x2B, 0xD4, 0x00}, |
| {0x2A, 0xD5, 0x00, // 9: dy>-dx |
| 0x7F, 0x80, 0x00, |
| 0xD4, 0x2B, 0x00}, |
| {0x30, 0x10, 0x00, // 10: dy<dx 2 |
| 0x50, 0xDF, 0x50, |
| 0x00, 0x10, 0x30}, |
| {0x00, 0x10, 0x30, // 11: dy<-dx 2 |
| 0x50, 0xDF, 0x50, |
| 0x30, 0x10, 0x00}, |
| {0xFF, 0xFF, 0x00, // 12: 2x2 |
| 0xFF, 0xFF, 0x00, |
| 0x00, 0x00, 0x00}, |
| {0xFF, 0xFF, 0xFF, // 13: 3x3 |
| 0xFF, 0xFF, 0xFF, |
| 0xFF, 0xFF, 0xFF}, |
| {0x00, 0x70, 0x20, // 14 |
| 0x70, 0xFF, 0x70, |
| 0x20, 0x70, 0x00}, |
| {0x00, 0x20, 0x60, // 15 |
| 0x20, 0xBF, 0x20, |
| 0x60, 0x20, 0x00}, |
| {0x60, 0x20, 0x00, // 16 |
| 0x20, 0xBF, 0x20, |
| 0x00, 0x20, 0x60}, |
| {0x00, 0x60, 0x04, // 17 |
| 0x00, 0x40, 0x60, |
| 0x00, 0x00, 0x3C}, |
| {0x00, 0x60, 0x00, // 18 |
| 0x00, 0x9F, 0x00, |
| 0x00, 0x00, 0x00}, |
| {0x00, 0x60, 0x00, // 19 |
| 0x00, 0x9F, 0x00, |
| 0x00, 0x00, 0x00}, |
| {0x00, 0x60, 0x00, // 20 |
| 0x00, 0x9F, 0x00, |
| 0x00, 0x00, 0x00}, |
| {0x00, 0x60, 0x00, // 21 |
| 0x00, 0x9F, 0x00, |
| 0x00, 0x00, 0x00}, |
| }; |
| const int testCount = sizeof(tests) / sizeof(tests[0]); |
| SkAssertResult(testCount == sizeof(results) / sizeof(results[0])); |
| int testFirst = test3by3_test < 0 ? 0 : test3by3_test; |
| int testLast = test3by3_test < 0 ? testCount : test3by3_test + 1; |
| for (int testIndex = testFirst; testIndex < testLast; ++testIndex) { |
| uint8_t result[pixelRow][pixelCol]; |
| sk_bzero(result, sizeof(result)); |
| const SkPoint* rect = tests[testIndex]; |
| SkAntiEdgeBuilder builder; |
| builder.process(rect, ptCount, result[0], pixelCol, pixelRow); |
| SkAssertResult(memcmp(results[testIndex], result[0], pixelCount) == 0); |
| } |
| } |
| |
| // input has arbitrary number of points |
| static void test_arbitrary_3_by_3() { |
| const int pixelRow = 3; |
| const int pixelCol = 3; |
| const int pixelCount = pixelRow * pixelCol; |
| const SkPoint t1[] = { {1,1}, {2,1}, {2,1.5f}, {1,1.5f}, {1,2}, {2,2}, |
| {2,1.5f}, {1,1.5f}, {1,1} }; |
| const SkPoint* tests[] = { t1 }; |
| size_t testPts[] = { sizeof(t1) / sizeof(t1[0]) }; |
| const uint8_t results[][pixelCount] = { |
| {0x00, 0x00, 0x00, // 0: 1 pixel rect |
| 0x00, 0xFF, 0x00, |
| 0x00, 0x00, 0x00}, |
| }; |
| const int testCount = sizeof(tests) / sizeof(tests[0]); |
| SkAssertResult(testCount == sizeof(results) / sizeof(results[0])); |
| int testFirst = test3by3_test < 0 ? 0 : test3by3_test; |
| int testLast = test3by3_test < 0 ? testCount : test3by3_test + 1; |
| for (int testIndex = testFirst; testIndex < testLast; ++testIndex) { |
| uint8_t result[pixelRow][pixelCol]; |
| sk_bzero(result, sizeof(result)); |
| const SkPoint* pts = tests[testIndex]; |
| size_t ptCount = testPts[testIndex]; |
| SkAntiEdgeBuilder builder; |
| builder.process(pts, ptCount, result[0], pixelCol, pixelRow); |
| SkAssertResult(memcmp(results[testIndex], result[0], pixelCount) == 0); |
| } |
| } |
| |
| #include "SkRect.h" |
| #include "SkPath.h" |
| |
| int testsweep_test; |
| |
| static void create_sweep(uint8_t* result, int pixelRow, int pixelCol, SkScalar rectWidth) { |
| const int ptCount = 4; |
| SkRect refRect = {pixelCol / 2 - rectWidth / 2, 5, |
| pixelCol / 2 + rectWidth / 2, pixelRow / 2 - 5}; |
| SkPath refPath; |
| refPath.addRect(refRect); |
| SkScalar angleFirst = testsweep_test < 0 ? 0 : testsweep_test; |
| SkScalar angleLast = testsweep_test < 0 ? 360 : testsweep_test + 1; |
| for (SkScalar angle = angleFirst; angle < angleLast; angle += 12) { |
| SkPath rotPath; |
| SkMatrix matrix; |
| matrix.setRotate(angle, SkIntToScalar(pixelCol) / 2, |
| SkIntToScalar(pixelRow) / 2); |
| refPath.transform(matrix, &rotPath); |
| SkPoint rect[ptCount], temp[2]; |
| SkPath::Iter iter(rotPath, false); |
| int index = 0; |
| for (;;) { |
| SkPath::Verb verb = iter.next(temp); |
| if (verb == SkPath::kMove_Verb) { |
| continue; |
| } |
| if (verb == SkPath::kClose_Verb) { |
| break; |
| } |
| SkAssertResult(SkPath::kLine_Verb == verb); |
| rect[index++] = temp[0]; |
| } |
| SkAntiEdgeBuilder builder; |
| builder.process(rect, ptCount, result, pixelCol, pixelRow); |
| } |
| } |
| |
| static void create_horz(uint8_t* result, int pixelRow, int pixelCol) { |
| const int ptCount = 4; |
| for (SkScalar x = 0; x < 100; x += 5) { |
| SkPoint rect[ptCount]; |
| rect[0].fX = 0; rect[0].fY = x; |
| rect[1].fX = 100; rect[1].fY = x; |
| rect[2].fX = 100; rect[2].fY = x + x / 50; |
| rect[3].fX = 0; rect[3].fY = x + x / 50; |
| SkAntiEdgeBuilder builder; |
| builder.process(rect, ptCount, result, pixelCol, pixelRow); |
| } |
| } |
| |
| static void create_vert(uint8_t* result, int pixelRow, int pixelCol) { |
| const int ptCount = 4; |
| for (SkScalar x = 0; x < 100; x += 5) { |
| SkPoint rect[ptCount]; |
| rect[0].fY = 0; rect[0].fX = x; |
| rect[1].fY = 100; rect[1].fX = x; |
| rect[2].fY = 100; rect[2].fX = x + x / 50; |
| rect[3].fY = 0; rect[3].fX = x + x / 50; |
| SkAntiEdgeBuilder builder; |
| builder.process(rect, ptCount, result, pixelCol, pixelRow); |
| } |
| } |
| |
| static void create_angle(uint8_t* result, int pixelRow, int pixelCol, SkScalar angle) { |
| const int ptCount = 4; |
| SkRect refRect = {25, 25, 125, 125}; |
| SkPath refPath; |
| for (SkScalar x = 30; x < 125; x += 5) { |
| refRect.fTop = x; |
| refRect.fBottom = x + (x - 25) / 50; |
| refPath.addRect(refRect); |
| } |
| SkPath rotPath; |
| SkMatrix matrix; |
| matrix.setRotate(angle, 75, 75); |
| refPath.transform(matrix, &rotPath); |
| SkPath::Iter iter(rotPath, false); |
| for (SkScalar x = 30; x < 125; x += 5) { |
| SkPoint rect[ptCount], temp[2]; |
| int index = 0; |
| for (;;) { |
| SkPath::Verb verb = iter.next(temp); |
| if (verb == SkPath::kMove_Verb) { |
| continue; |
| } |
| if (verb == SkPath::kClose_Verb) { |
| break; |
| } |
| SkAssertResult(SkPath::kLine_Verb == verb); |
| rect[index++] = temp[0]; |
| } |
| // if ((x == 30 || x == 75) && angle == 12) continue; |
| SkAntiEdgeBuilder builder; |
| builder.process(rect, ptCount, result, pixelCol, pixelRow); |
| } |
| } |
| |
| static void test_sweep() { |
| const int pixelRow = 100; |
| const int pixelCol = 100; |
| uint8_t result[pixelRow][pixelCol]; |
| sk_bzero(result, sizeof(result)); |
| create_sweep(result[0], pixelRow, pixelCol, 1); |
| } |
| |
| static void test_horz() { |
| const int pixelRow = 100; |
| const int pixelCol = 100; |
| uint8_t result[pixelRow][pixelCol]; |
| sk_bzero(result, sizeof(result)); |
| create_horz(result[0], pixelRow, pixelCol); |
| } |
| |
| static void test_vert() { |
| const int pixelRow = 100; |
| const int pixelCol = 100; |
| uint8_t result[pixelRow][pixelCol]; |
| sk_bzero(result, sizeof(result)); |
| create_vert(result[0], pixelRow, pixelCol); |
| } |
| |
| static void test_angle(SkScalar angle) { |
| const int pixelRow = 150; |
| const int pixelCol = 150; |
| uint8_t result[pixelRow][pixelCol]; |
| sk_bzero(result, sizeof(result)); |
| create_angle(result[0], pixelRow, pixelCol, angle); |
| } |
| |
| #include "SkBitmap.h" |
| |
| void CreateSweep(SkBitmap* sweep, SkScalar rectWidth) { |
| const int pixelRow = 100; |
| const int pixelCol = 100; |
| sweep->setConfig(SkBitmap::kA8_Config, pixelCol, pixelRow); |
| sweep->allocPixels(); |
| sweep->eraseColor(SK_ColorTRANSPARENT); |
| sweep->lockPixels(); |
| void* pixels = sweep->getPixels(); |
| create_sweep((uint8_t*) pixels, pixelRow, pixelCol, rectWidth); |
| sweep->unlockPixels(); |
| } |
| |
| void CreateHorz(SkBitmap* sweep) { |
| const int pixelRow = 100; |
| const int pixelCol = 100; |
| sweep->setConfig(SkBitmap::kA8_Config, pixelCol, pixelRow); |
| sweep->allocPixels(); |
| sweep->eraseColor(SK_ColorTRANSPARENT); |
| sweep->lockPixels(); |
| void* pixels = sweep->getPixels(); |
| create_horz((uint8_t*) pixels, pixelRow, pixelCol); |
| sweep->unlockPixels(); |
| } |
| |
| void CreateVert(SkBitmap* sweep) { |
| const int pixelRow = 100; |
| const int pixelCol = 100; |
| sweep->setConfig(SkBitmap::kA8_Config, pixelCol, pixelRow); |
| sweep->allocPixels(); |
| sweep->eraseColor(SK_ColorTRANSPARENT); |
| sweep->lockPixels(); |
| void* pixels = sweep->getPixels(); |
| create_vert((uint8_t*) pixels, pixelRow, pixelCol); |
| sweep->unlockPixels(); |
| } |
| |
| void CreateAngle(SkBitmap* sweep, SkScalar angle) { |
| const int pixelRow = 150; |
| const int pixelCol = 150; |
| sweep->setConfig(SkBitmap::kA8_Config, pixelCol, pixelRow); |
| sweep->allocPixels(); |
| sweep->eraseColor(SK_ColorTRANSPARENT); |
| sweep->lockPixels(); |
| void* pixels = sweep->getPixels(); |
| create_angle((uint8_t*) pixels, pixelRow, pixelCol, angle); |
| sweep->unlockPixels(); |
| } |
| |
| #include "SkCanvas.h" |
| |
| static void testPng() { |
| SkBitmap device; |
| device.setConfig(SkBitmap::kARGB_8888_Config, 4, 4); |
| device.allocPixels(); |
| device.eraseColor(0xFFFFFFFF); |
| |
| SkCanvas canvas(device); |
| canvas.drawARGB(167, 0, 0, 0); |
| |
| device.lockPixels(); |
| unsigned char* pixels = (unsigned char*) device.getPixels(); |
| SkDebugf("%02x%02x%02x%02x", pixels[3], pixels[2], pixels[1], pixels[0]); |
| } |
| |
| void SkAntiEdge_Test() { |
| testPng(); |
| test_arbitrary_3_by_3(); |
| test_angle(12); |
| #if 0 |
| test3by3_test = 18; |
| #else |
| test3by3_test = -1; |
| #endif |
| #if 0 |
| testsweep_test = 7 * 12; |
| #else |
| testsweep_test = -1; |
| #endif |
| if (testsweep_test == -1) { |
| test_3_by_3(); |
| } |
| test_sweep(); |
| test_horz(); |
| test_vert(); |
| } |