/* * 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& 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 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 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 (dyfNext) { 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: dydx {{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: dydx 0x80, 0x7F, 0x00, 0x2B, 0xD4, 0x00}, {0x2A, 0xD5, 0x00, // 9: dy>-dx 0x7F, 0x80, 0x00, 0xD4, 0x2B, 0x00}, {0x30, 0x10, 0x00, // 10: dysetConfig(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(); }