blob: 9250d02f079607b164ff60747f5c6f3276bfa2ab [file] [log] [blame]
Yuqian Lidf60e362017-07-25 11:26:31 -04001/*
2 * Copyright 2016 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkAnalyticEdge.h"
9#include "SkAntiRun.h"
10#include "SkAutoMalloc.h"
11#include "SkBlitter.h"
12#include "SkCoverageDelta.h"
13#include "SkEdge.h"
14#include "SkEdgeBuilder.h"
15#include "SkGeometry.h"
16#include "SkMask.h"
17#include "SkPath.h"
18#include "SkQuadClipper.h"
19#include "SkRasterClip.h"
20#include "SkRegion.h"
21#include "SkScan.h"
22#include "SkScanPriv.h"
23#include "SkTSort.h"
24#include "SkTemplates.h"
25#include "SkUtils.h"
26
27///////////////////////////////////////////////////////////////////////////////
28
29/*
30
31DAA stands for Delta-based Anti-Aliasing.
32
33This is an improved version of our analytic AA algorithm (in SkScan_AAAPath.cpp)
34which first scan convert paths into coverage deltas (this step can happen out of order,
35and we don't seem to be needed to worry about the intersection, clamping, etc.),
36and then use a single blitter run to convert all those deltas into the final alphas.
37
38Before we go to the final blitter run, we'll use SkFixed for all delta values so we
39don't ever have to worry about under/overflow.
40
41*/
42
43///////////////////////////////////////////////////////////////////////////////
44
45// The following helper functions are the same as those from SkScan_AAAPath.cpp
46// except that we use SkFixed everywhere instead of SkAlpha.
47
48static inline SkFixed SkFixedMul_lowprec(SkFixed a, SkFixed b) {
49 return (a >> 8) * (b >> 8);
50}
51
52// Return the alpha of a trapezoid whose height is 1
53static inline SkFixed trapezoidToAlpha(SkFixed l1, SkFixed l2) {
54 SkASSERT(l1 >= 0 && l2 >= 0);
55 return (l1 + l2) >> 1;
56}
57
58// The alpha of right-triangle (a, a*b)
59static inline SkFixed partialTriangleToAlpha(SkFixed a, SkFixed b) {
60 SkASSERT(a <= SK_Fixed1);
61 // SkFixedMul(SkFixedMul(a, a), b) >> 1
62 // return ((((a >> 8) * (a >> 8)) >> 8) * (b >> 8)) >> 1;
63 return (a >> 11) * (a >> 11) * (b >> 11);
64}
65
66static inline SkFixed getPartialAlpha(SkFixed alpha, SkFixed partialMultiplier) {
67 // DAA should not be so sensitive to the precision (compared to AAA).
68 return SkFixedMul_lowprec(alpha, partialMultiplier);
69}
70
71///////////////////////////////////////////////////////////////////////////////
72
73template<bool isPartial, class Deltas>
74static inline void add_coverage_delta_segment(int y, SkFixed rowHeight, const SkAnalyticEdge* edge,
75 SkFixed nextX, Deltas* deltas) { // rowHeight=fullAlpha
76 SkASSERT(rowHeight <= SK_Fixed1 && rowHeight >= 0);
77
78 // Let's see if multiplying sign is faster than multiplying edge->fWinding.
79 // (Compiler should be able to optimize multiplication with 1/-1?)
80 int sign = edge->fWinding == 1 ? 1 : -1;
81
82 SkFixed l = SkTMin(edge->fX, nextX);
83 SkFixed r = edge->fX + nextX - l;
84 int L = SkFixedFloorToInt(l);
85 int R = SkFixedCeilToInt(r);
86 int len = R - L;
87
88 switch (len) {
89 case 0: {
90 deltas->addDelta(L, y, rowHeight * sign);
91 return;
92 }
93 case 1: {
94 SkFixed fixedR = SkIntToFixed(R);
95 SkFixed alpha = trapezoidToAlpha(fixedR - l, fixedR - r);
96 if (isPartial) {
97 alpha = getPartialAlpha(alpha, rowHeight);
98 }
99 deltas->addDelta(L, y, alpha * sign);
100 deltas->addDelta(L + 1, y, (rowHeight - alpha) * sign);
101 return;
102 }
103 case 2: {
104 SkFixed middle = SkIntToFixed(L + 1);
105 SkFixed x1 = middle - l;
106 SkFixed x2 = r - middle;
107 SkFixed alpha1 = partialTriangleToAlpha(x1, edge->fDY);
108 SkFixed alpha2 = rowHeight - partialTriangleToAlpha(x2, edge->fDY);
109 deltas->addDelta(L, y, alpha1 * sign);
110 deltas->addDelta(L + 1, y, (alpha2 - alpha1) * sign);
111 deltas->addDelta(L + 2, y, (rowHeight - alpha2) * sign);
112 return;
113 }
114 }
115
116 // When len > 2, computations are similar to computeAlphaAboveLine in SkScan_AAAPath.cpp
117 SkFixed dY = edge->fDY;
118 SkFixed fixedL = SkIntToFixed(L);
119 SkFixed fixedR = SkIntToFixed(R);
120 SkFixed first = SK_Fixed1 + fixedL - l; // horizontal edge of the left-most triangle
121 SkFixed last = r - (fixedR - SK_Fixed1); // horizontal edge of the right-most triangle
122 SkFixed firstH = SkFixedMul_lowprec(first, dY); // vertical edge of the left-most triangle
123
124 SkFixed alpha0 = SkFixedMul_lowprec(first, firstH) >> 1; // triangle alpha
125 SkFixed alpha1 = firstH + (dY >> 1); // rectangle plus triangle
126 deltas->addDelta(L, y, alpha0 * sign);
127 deltas->addDelta(L + 1, y, (alpha1 - alpha0) * sign);
128 for(int i = 2; i < len - 1; ++i) {
129 deltas->addDelta(L + i, y, dY * sign); // the increment is always a rect of height = dY
130 }
131
132 SkFixed alphaR2 = alpha1 + dY * (len - 3); // the alpha at R - 2
133 SkFixed lastAlpha = rowHeight - partialTriangleToAlpha(last, dY); // the alpha at R - 1
134 deltas->addDelta(R - 1, y, (lastAlpha - alphaR2) * sign);
135 deltas->addDelta(R, y, (rowHeight - lastAlpha) * sign);
136}
137
138class XLessThan {
139public:
140 bool operator()(const SkBezier* a, const SkBezier* b) {
141 return a->fP0.fX + a->fP1.fX < b->fP0.fX + b->fP1.fX;
142 }
143};
144
145class YLessThan {
146public:
147 bool operator()(const SkBezier* a, const SkBezier* b) {
148 return a->fP0.fY + a->fP1.fY < b->fP0.fY + b->fP1.fY;
149 }
150};
151
152template<class Deltas> static SK_ALWAYS_INLINE
153void gen_alpha_deltas(const SkPath& path, const SkRegion& clipRgn, Deltas& result,
154 SkBlitter* blitter, bool skipRect, bool pathContainedInClip) {
155 // 1. Build edges
156 SkEdgeBuilder builder;
157 SkIRect ir = path.getBounds().roundOut();
158 const SkIRect& clipRect = clipRgn.getBounds();
159 int count = builder.build_edges(path, &clipRect, 0, pathContainedInClip,
160 SkEdgeBuilder::kBezier);
161 if (count == 0) {
162 return;
163 }
164 SkBezier** list = builder.bezierList();
165
166 // 2. Try to find the rect part because blitAntiRect is so much faster than blitCoverageDeltas
167 int rectTop = ir.fBottom; // the rect is initialized to be empty as top = bot
168 int rectBot = ir.fBottom;
169 if (skipRect) { // only find that rect is skipRect == true
170 YLessThan lessThan; // sort edges in YX order
171 SkTQSort(list, list + count - 1, lessThan);
172 for(int i = 0; i < count - 1; ++i) {
173 SkBezier* lb = list[i];
174 SkBezier* rb = list[i + 1];
175
Yuqian Lic5e4e742017-09-18 14:38:43 -0400176 // fCount == 2 ensures that lb and rb are lines instead of quads or cubics.
177 bool lDX0 = lb->fP0.fX == lb->fP1.fX && lb->fCount == 2;
178 bool rDX0 = rb->fP0.fX == rb->fP1.fX && rb->fCount == 2;
Yuqian Lidf60e362017-07-25 11:26:31 -0400179 if (!lDX0 || !rDX0) { // make sure that the edges are vertical
180 continue;
181 }
182
183 SkAnalyticEdge l, r;
184 l.setLine(lb->fP0, lb->fP1);
185 r.setLine(rb->fP0, rb->fP1);
186
187 SkFixed xorUpperY = l.fUpperY ^ r.fUpperY;
188 SkFixed xorLowerY = l.fLowerY ^ r.fLowerY;
189 if ((xorUpperY | xorLowerY) == 0) { // equal upperY and lowerY
190 rectTop = SkFixedCeilToInt(l.fUpperY);
191 rectBot = SkFixedFloorToInt(l.fLowerY);
192 if (rectBot > rectTop) { // if bot == top, the rect is too short for blitAntiRect
193 int L = SkFixedCeilToInt(l.fUpperX);
194 int R = SkFixedFloorToInt(r.fUpperX);
195 if (L <= R) {
196 SkAlpha la = (SkIntToFixed(L) - l.fUpperX) >> 8;
197 SkAlpha ra = (r.fUpperX - SkIntToFixed(R)) >> 8;
198 result.setAntiRect(L - 1, rectTop, R - L, rectBot - rectTop, la, ra);
199 } else { // too thin to use blitAntiRect; reset the rect region to be emtpy
200 rectTop = rectBot = ir.fBottom;
201 }
202 }
203 break;
204 }
205
206 }
207 }
208
209 // 3. Sort edges in x so we may need less sorting for delta based on x. This only helps
210 // SkCoverageDeltaList. And we don't want to sort more than SORT_THRESHOLD edges where
211 // the log(count) factor of the quick sort may become a bottleneck; when there are so
212 // many edges, we're unlikely to make deltas sorted anyway.
Yuqian Lidb96f252017-08-02 10:32:55 -0400213 constexpr int SORT_THRESHOLD = 256;
Yuqian Lidf60e362017-07-25 11:26:31 -0400214 if (std::is_same<Deltas, SkCoverageDeltaList>::value && count < SORT_THRESHOLD) {
215 XLessThan lessThan;
216 SkTQSort(list, list + count - 1, lessThan);
217 }
218
219 // Future todo: parallize and SIMD the following code.
220 // 4. iterate through edges and generate deltas
221 for(int index = 0; index < count; ++index) {
222 SkAnalyticCubicEdge storage;
223 SkASSERT(sizeof(SkAnalyticQuadraticEdge) >= sizeof(SkAnalyticEdge));
224 SkASSERT(sizeof(SkAnalyticCubicEdge) >= sizeof(SkAnalyticQuadraticEdge));
225
226 SkBezier* bezier = list[index];
227 SkAnalyticEdge* currE = &storage;
228 bool edgeSet = false;
229
Yuqian Li5eb8fc52017-08-08 14:00:52 -0400230 int originalWinding = 1;
231 bool sortY = true;
Yuqian Lidf60e362017-07-25 11:26:31 -0400232 switch (bezier->fCount) {
233 case 2: {
234 edgeSet = currE->setLine(bezier->fP0, bezier->fP1);
Yuqian Li5eb8fc52017-08-08 14:00:52 -0400235 originalWinding = currE->fWinding;
Yuqian Lidf60e362017-07-25 11:26:31 -0400236 break;
237 }
238 case 3: {
239 SkQuad* quad = static_cast<SkQuad*>(bezier);
240 SkPoint pts[3] = {quad->fP0, quad->fP1, quad->fP2};
241 edgeSet = static_cast<SkAnalyticQuadraticEdge*>(currE)->setQuadratic(pts);
Yuqian Li5eb8fc52017-08-08 14:00:52 -0400242 originalWinding = static_cast<SkAnalyticQuadraticEdge*>(currE)->fQEdge.fWinding;
Yuqian Lidf60e362017-07-25 11:26:31 -0400243 break;
244 }
245 case 4: {
Yuqian Li5eb8fc52017-08-08 14:00:52 -0400246 sortY = false;
Yuqian Lidf60e362017-07-25 11:26:31 -0400247 SkCubic* cubic = static_cast<SkCubic*>(bezier);
248 SkPoint pts[4] = {cubic->fP0, cubic->fP1, cubic->fP2, cubic->fP3};
Yuqian Li5eb8fc52017-08-08 14:00:52 -0400249 edgeSet = static_cast<SkAnalyticCubicEdge*>(currE)->setCubic(pts, sortY);
250 originalWinding = static_cast<SkAnalyticCubicEdge*>(currE)->fCEdge.fWinding;
Yuqian Lidf60e362017-07-25 11:26:31 -0400251 break;
252 }
253 }
254
255 if (!edgeSet) {
256 continue;
257 }
258
259 do {
260 currE->fX = currE->fUpperX;
261
262 SkFixed upperFloor = SkFixedFloorToFixed(currE->fUpperY);
263 SkFixed lowerCeil = SkFixedCeilToFixed(currE->fLowerY);
264 int iy = SkFixedFloorToInt(upperFloor);
265
266 if (lowerCeil <= upperFloor + SK_Fixed1) { // only one row is affected by the currE
267 SkFixed rowHeight = currE->fLowerY - currE->fUpperY;
268 SkFixed nextX = currE->fX + SkFixedMul(currE->fDX, rowHeight);
Yuqian Li5eb8fc52017-08-08 14:00:52 -0400269 if (iy >= clipRect.fTop && iy < clipRect.fBottom) {
270 add_coverage_delta_segment<true>(iy, rowHeight, currE, nextX, &result);
271 }
Yuqian Lidf60e362017-07-25 11:26:31 -0400272 continue;
273 }
274
275 // check first row
276 SkFixed rowHeight = upperFloor + SK_Fixed1 - currE->fUpperY;
277 SkFixed nextX;
278 if (rowHeight != SK_Fixed1) { // it's a partial row
279 nextX = currE->fX + SkFixedMul(currE->fDX, rowHeight);
280 add_coverage_delta_segment<true>(iy, rowHeight, currE, nextX, &result);
281 } else { // it's a full row so we can leave it to the while loop
282 iy--; // compensate the iy++ in the while loop
283 nextX = currE->fX;
284 }
285
286 while (true) { // process the full rows in the middle
287 iy++;
288 SkFixed y = SkIntToFixed(iy);
289 currE->fX = nextX;
290 nextX += currE->fDX;
291
292 if (y + SK_Fixed1 > currE->fLowerY) {
293 break; // no full rows left, break
294 }
295
296 // Check whether we're in the rect part that will be covered by blitAntiRect
297 if (iy >= rectTop && iy < rectBot) {
298 SkASSERT(currE->fDX == 0); // If yes, we must be on an edge with fDX = 0.
299 iy = rectBot - 1; // Skip the rect part by advancing iy to the bottom.
300 continue;
301 }
302
303 // Add current edge's coverage deltas on this full row
304 add_coverage_delta_segment<false>(iy, SK_Fixed1, currE, nextX, &result);
305 }
306
307 // last partial row
Yuqian Li5eb8fc52017-08-08 14:00:52 -0400308 if (SkIntToFixed(iy) < currE->fLowerY && iy >= clipRect.fTop && iy < clipRect.fBottom) {
Yuqian Lidf60e362017-07-25 11:26:31 -0400309 rowHeight = currE->fLowerY - SkIntToFixed(iy);
310 nextX = currE->fX + SkFixedMul(currE->fDX, rowHeight);
311 add_coverage_delta_segment<true>(iy, rowHeight, currE, nextX, &result);
312 }
Yuqian Li5eb8fc52017-08-08 14:00:52 -0400313 // Intended assignment to fWinding to restore the maybe-negated winding (during updateLine)
314 } while ((currE->fWinding = originalWinding) && currE->update(currE->fLowerY, sortY));
Yuqian Lidf60e362017-07-25 11:26:31 -0400315 }
316}
317
318void SkScan::DAAFillPath(const SkPath& path, const SkRegion& origClip, SkBlitter* blitter,
319 bool forceRLE) {
320
321 FillPathFunc fillPathFunc = [](const SkPath& path, SkBlitter* blitter, bool isInverse,
322 const SkIRect& ir, const SkRegion* clipRgn, const SkIRect* clipRect, bool forceRLE){
323 bool isEvenOdd = path.getFillType() & 1;
324 bool isConvex = path.isConvex();
325 bool skipRect = isConvex && !isInverse;
326
327 const SkIRect& clipBounds = clipRgn->getBounds();
328 SkIRect clippedIR = ir;
329 clippedIR.intersect(clipBounds);
330
Mike Kleinad04a4b2017-09-18 19:29:24 +0000331 SkRect rect;
332 if (!isInverse && path.isRect(&rect) && clippedIR.height() >= 3 && clippedIR.width() >= 3) {
333 // The overhead of even constructing SkCoverageDeltaList/Mask is too big. So just blit.
334 bool nonEmpty = rect.intersect(SkRect::Make(clipBounds));
335 SkASSERT(nonEmpty); // do_fill_path should have already handled the empty case
336 if (nonEmpty) {
337 blitter->blitFatAntiRect(rect);
338 }
Yuqian Lidf60e362017-07-25 11:26:31 -0400339 return;
340 }
341
Yuqian Libd40a5b2017-09-01 15:38:45 -0400342#ifdef GOOGLE3
343 constexpr int STACK_SIZE = 12 << 10; // 12K stack size alloc; Google3 has 16K limit.
344#else
345 constexpr int STACK_SIZE = 64 << 10; // 64k stack size to avoid heap allocation
346#endif
347 SkSTArenaAlloc<STACK_SIZE> alloc; // avoid heap allocation with SkSTArenaAlloc
348
Yuqian Lidf60e362017-07-25 11:26:31 -0400349 // Only blitter->blitXXX need to be done in order in the threaded backend.
350 // Everything before can be done out of order in the threaded backend.
351 if (!forceRLE && !isInverse && SkCoverageDeltaMask::Suitable(clippedIR)) {
Yuqian Libd40a5b2017-09-01 15:38:45 -0400352 SkCoverageDeltaMask deltaMask(&alloc, clippedIR);
Yuqian Lidf60e362017-07-25 11:26:31 -0400353 gen_alpha_deltas(path, *clipRgn, deltaMask, blitter, skipRect, clipRect == nullptr);
354 deltaMask.convertCoverageToAlpha(isEvenOdd, isInverse, isConvex);
355 blitter->blitMask(deltaMask.prepareSkMask(), clippedIR);
356 } else {
Yuqian Lidf60e362017-07-25 11:26:31 -0400357 SkCoverageDeltaList deltaList(&alloc, clippedIR.fTop, clippedIR.fBottom, forceRLE);
358 gen_alpha_deltas(path, *clipRgn, deltaList, blitter, skipRect, clipRect == nullptr);
359 blitter->blitCoverageDeltas(&deltaList, clipBounds, isEvenOdd, isInverse, isConvex);
360 }
361 };
362
363 // For threaded backend with out-of-order init-once (and therefore out-of-order do_fill_path),
364 // we probably have to take care of the blitRegion, sk_blit_above, sk_blit_below in do_fill_path
365 // to maintain the draw order. If we do that, be caureful that blitRect may throw exception is
366 // the rect is empty.
367 do_fill_path(path, origClip, blitter, forceRLE, 2, std::move(fillPathFunc));
368}