blob: 8f21902137c830c16f900f94dcd37907f8f23aec [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"
Hal Canaryea60b952018-08-21 11:45:46 -040025#include "SkUTF.h"
Yuqian Lidf60e362017-07-25 11:26:31 -040026
Kevin Lubickf20c3492018-10-17 09:46:00 -040027#if defined(SK_DISABLE_DAA)
28void SkScan::DAAFillPath(const SkPath& path, SkBlitter* blitter, const SkIRect& ir,
29 const SkIRect& clipBounds, bool forceRLE, SkDAARecord* record) {
30 SkDEBUGFAIL("DAA Disabled");
31 return;
32}
33#else
Yuqian Lidf60e362017-07-25 11:26:31 -040034///////////////////////////////////////////////////////////////////////////////
35
36/*
37
38DAA stands for Delta-based Anti-Aliasing.
39
40This is an improved version of our analytic AA algorithm (in SkScan_AAAPath.cpp)
41which first scan convert paths into coverage deltas (this step can happen out of order,
42and we don't seem to be needed to worry about the intersection, clamping, etc.),
43and then use a single blitter run to convert all those deltas into the final alphas.
44
45Before we go to the final blitter run, we'll use SkFixed for all delta values so we
46don't ever have to worry about under/overflow.
47
48*/
49
50///////////////////////////////////////////////////////////////////////////////
51
52// The following helper functions are the same as those from SkScan_AAAPath.cpp
53// except that we use SkFixed everywhere instead of SkAlpha.
54
55static inline SkFixed SkFixedMul_lowprec(SkFixed a, SkFixed b) {
56 return (a >> 8) * (b >> 8);
57}
58
59// Return the alpha of a trapezoid whose height is 1
60static inline SkFixed trapezoidToAlpha(SkFixed l1, SkFixed l2) {
61 SkASSERT(l1 >= 0 && l2 >= 0);
62 return (l1 + l2) >> 1;
63}
64
65// The alpha of right-triangle (a, a*b)
66static inline SkFixed partialTriangleToAlpha(SkFixed a, SkFixed b) {
67 SkASSERT(a <= SK_Fixed1);
68 // SkFixedMul(SkFixedMul(a, a), b) >> 1
69 // return ((((a >> 8) * (a >> 8)) >> 8) * (b >> 8)) >> 1;
70 return (a >> 11) * (a >> 11) * (b >> 11);
71}
72
73static inline SkFixed getPartialAlpha(SkFixed alpha, SkFixed partialMultiplier) {
74 // DAA should not be so sensitive to the precision (compared to AAA).
75 return SkFixedMul_lowprec(alpha, partialMultiplier);
76}
77
78///////////////////////////////////////////////////////////////////////////////
79
80template<bool isPartial, class Deltas>
81static inline void add_coverage_delta_segment(int y, SkFixed rowHeight, const SkAnalyticEdge* edge,
82 SkFixed nextX, Deltas* deltas) { // rowHeight=fullAlpha
83 SkASSERT(rowHeight <= SK_Fixed1 && rowHeight >= 0);
84
85 // Let's see if multiplying sign is faster than multiplying edge->fWinding.
86 // (Compiler should be able to optimize multiplication with 1/-1?)
87 int sign = edge->fWinding == 1 ? 1 : -1;
88
89 SkFixed l = SkTMin(edge->fX, nextX);
90 SkFixed r = edge->fX + nextX - l;
91 int L = SkFixedFloorToInt(l);
92 int R = SkFixedCeilToInt(r);
93 int len = R - L;
94
95 switch (len) {
96 case 0: {
97 deltas->addDelta(L, y, rowHeight * sign);
98 return;
99 }
100 case 1: {
101 SkFixed fixedR = SkIntToFixed(R);
102 SkFixed alpha = trapezoidToAlpha(fixedR - l, fixedR - r);
103 if (isPartial) {
104 alpha = getPartialAlpha(alpha, rowHeight);
105 }
106 deltas->addDelta(L, y, alpha * sign);
107 deltas->addDelta(L + 1, y, (rowHeight - alpha) * sign);
108 return;
109 }
110 case 2: {
111 SkFixed middle = SkIntToFixed(L + 1);
112 SkFixed x1 = middle - l;
113 SkFixed x2 = r - middle;
114 SkFixed alpha1 = partialTriangleToAlpha(x1, edge->fDY);
115 SkFixed alpha2 = rowHeight - partialTriangleToAlpha(x2, edge->fDY);
116 deltas->addDelta(L, y, alpha1 * sign);
117 deltas->addDelta(L + 1, y, (alpha2 - alpha1) * sign);
118 deltas->addDelta(L + 2, y, (rowHeight - alpha2) * sign);
119 return;
120 }
121 }
122
123 // When len > 2, computations are similar to computeAlphaAboveLine in SkScan_AAAPath.cpp
124 SkFixed dY = edge->fDY;
125 SkFixed fixedL = SkIntToFixed(L);
126 SkFixed fixedR = SkIntToFixed(R);
127 SkFixed first = SK_Fixed1 + fixedL - l; // horizontal edge of the left-most triangle
128 SkFixed last = r - (fixedR - SK_Fixed1); // horizontal edge of the right-most triangle
129 SkFixed firstH = SkFixedMul_lowprec(first, dY); // vertical edge of the left-most triangle
130
131 SkFixed alpha0 = SkFixedMul_lowprec(first, firstH) >> 1; // triangle alpha
132 SkFixed alpha1 = firstH + (dY >> 1); // rectangle plus triangle
133 deltas->addDelta(L, y, alpha0 * sign);
134 deltas->addDelta(L + 1, y, (alpha1 - alpha0) * sign);
135 for(int i = 2; i < len - 1; ++i) {
136 deltas->addDelta(L + i, y, dY * sign); // the increment is always a rect of height = dY
137 }
138
139 SkFixed alphaR2 = alpha1 + dY * (len - 3); // the alpha at R - 2
140 SkFixed lastAlpha = rowHeight - partialTriangleToAlpha(last, dY); // the alpha at R - 1
141 deltas->addDelta(R - 1, y, (lastAlpha - alphaR2) * sign);
142 deltas->addDelta(R, y, (rowHeight - lastAlpha) * sign);
143}
144
145class XLessThan {
146public:
147 bool operator()(const SkBezier* a, const SkBezier* b) {
148 return a->fP0.fX + a->fP1.fX < b->fP0.fX + b->fP1.fX;
149 }
150};
151
152class YLessThan {
153public:
154 bool operator()(const SkBezier* a, const SkBezier* b) {
155 return a->fP0.fY + a->fP1.fY < b->fP0.fY + b->fP1.fY;
156 }
157};
158
159template<class Deltas> static SK_ALWAYS_INLINE
Yuqian Li8b0ef632018-03-08 13:39:27 -0500160void gen_alpha_deltas(const SkPath& path, const SkIRect& clippedIR, const SkIRect& clipBounds,
161 Deltas& result, SkBlitter* blitter, bool skipRect, bool pathContainedInClip) {
Yuqian Lidf60e362017-07-25 11:26:31 -0400162 // 1. Build edges
163 SkEdgeBuilder builder;
Yuqian Li8b0ef632018-03-08 13:39:27 -0500164 // We have to use clipBounds instead of clippedIR to build edges because of "canCullToTheRight":
165 // if the builder finds a right edge past the right clip, it won't build that right edge.
166 int count = builder.build_edges(path, &clipBounds, 0, pathContainedInClip,
167 SkEdgeBuilder::kBezier);
168
Yuqian Lidf60e362017-07-25 11:26:31 -0400169 if (count == 0) {
170 return;
171 }
172 SkBezier** list = builder.bezierList();
173
174 // 2. Try to find the rect part because blitAntiRect is so much faster than blitCoverageDeltas
Yuqian Li8b0ef632018-03-08 13:39:27 -0500175 int rectTop = clippedIR.fBottom; // the rect is initialized to be empty as top = bot
176 int rectBot = clippedIR.fBottom;
Yuqian Lidf60e362017-07-25 11:26:31 -0400177 if (skipRect) { // only find that rect is skipRect == true
178 YLessThan lessThan; // sort edges in YX order
179 SkTQSort(list, list + count - 1, lessThan);
180 for(int i = 0; i < count - 1; ++i) {
181 SkBezier* lb = list[i];
182 SkBezier* rb = list[i + 1];
183
Yuqian Lic5e4e742017-09-18 14:38:43 -0400184 // fCount == 2 ensures that lb and rb are lines instead of quads or cubics.
185 bool lDX0 = lb->fP0.fX == lb->fP1.fX && lb->fCount == 2;
186 bool rDX0 = rb->fP0.fX == rb->fP1.fX && rb->fCount == 2;
Yuqian Lidf60e362017-07-25 11:26:31 -0400187 if (!lDX0 || !rDX0) { // make sure that the edges are vertical
188 continue;
189 }
190
191 SkAnalyticEdge l, r;
Yuqian Li9ea47be2018-05-31 15:18:54 -0700192 if (!l.setLine(lb->fP0, lb->fP1) || !r.setLine(rb->fP0, rb->fP1)) {
193 continue;
194 }
Yuqian Lidf60e362017-07-25 11:26:31 -0400195
196 SkFixed xorUpperY = l.fUpperY ^ r.fUpperY;
197 SkFixed xorLowerY = l.fLowerY ^ r.fLowerY;
198 if ((xorUpperY | xorLowerY) == 0) { // equal upperY and lowerY
199 rectTop = SkFixedCeilToInt(l.fUpperY);
200 rectBot = SkFixedFloorToInt(l.fLowerY);
201 if (rectBot > rectTop) { // if bot == top, the rect is too short for blitAntiRect
202 int L = SkFixedCeilToInt(l.fUpperX);
203 int R = SkFixedFloorToInt(r.fUpperX);
204 if (L <= R) {
205 SkAlpha la = (SkIntToFixed(L) - l.fUpperX) >> 8;
206 SkAlpha ra = (r.fUpperX - SkIntToFixed(R)) >> 8;
207 result.setAntiRect(L - 1, rectTop, R - L, rectBot - rectTop, la, ra);
208 } else { // too thin to use blitAntiRect; reset the rect region to be emtpy
Yuqian Li8b0ef632018-03-08 13:39:27 -0500209 rectTop = rectBot = clippedIR.fBottom;
Yuqian Lidf60e362017-07-25 11:26:31 -0400210 }
211 }
212 break;
213 }
214
215 }
216 }
217
218 // 3. Sort edges in x so we may need less sorting for delta based on x. This only helps
219 // SkCoverageDeltaList. And we don't want to sort more than SORT_THRESHOLD edges where
220 // the log(count) factor of the quick sort may become a bottleneck; when there are so
221 // many edges, we're unlikely to make deltas sorted anyway.
Yuqian Lidb96f252017-08-02 10:32:55 -0400222 constexpr int SORT_THRESHOLD = 256;
Yuqian Lidf60e362017-07-25 11:26:31 -0400223 if (std::is_same<Deltas, SkCoverageDeltaList>::value && count < SORT_THRESHOLD) {
224 XLessThan lessThan;
225 SkTQSort(list, list + count - 1, lessThan);
226 }
227
228 // Future todo: parallize and SIMD the following code.
229 // 4. iterate through edges and generate deltas
230 for(int index = 0; index < count; ++index) {
231 SkAnalyticCubicEdge storage;
232 SkASSERT(sizeof(SkAnalyticQuadraticEdge) >= sizeof(SkAnalyticEdge));
233 SkASSERT(sizeof(SkAnalyticCubicEdge) >= sizeof(SkAnalyticQuadraticEdge));
234
235 SkBezier* bezier = list[index];
236 SkAnalyticEdge* currE = &storage;
237 bool edgeSet = false;
238
Yuqian Li5eb8fc52017-08-08 14:00:52 -0400239 int originalWinding = 1;
240 bool sortY = true;
Yuqian Lidf60e362017-07-25 11:26:31 -0400241 switch (bezier->fCount) {
242 case 2: {
243 edgeSet = currE->setLine(bezier->fP0, bezier->fP1);
Yuqian Li5eb8fc52017-08-08 14:00:52 -0400244 originalWinding = currE->fWinding;
Yuqian Lidf60e362017-07-25 11:26:31 -0400245 break;
246 }
247 case 3: {
248 SkQuad* quad = static_cast<SkQuad*>(bezier);
249 SkPoint pts[3] = {quad->fP0, quad->fP1, quad->fP2};
250 edgeSet = static_cast<SkAnalyticQuadraticEdge*>(currE)->setQuadratic(pts);
Yuqian Li5eb8fc52017-08-08 14:00:52 -0400251 originalWinding = static_cast<SkAnalyticQuadraticEdge*>(currE)->fQEdge.fWinding;
Yuqian Lidf60e362017-07-25 11:26:31 -0400252 break;
253 }
254 case 4: {
Yuqian Li5eb8fc52017-08-08 14:00:52 -0400255 sortY = false;
Yuqian Lidf60e362017-07-25 11:26:31 -0400256 SkCubic* cubic = static_cast<SkCubic*>(bezier);
257 SkPoint pts[4] = {cubic->fP0, cubic->fP1, cubic->fP2, cubic->fP3};
Yuqian Li5eb8fc52017-08-08 14:00:52 -0400258 edgeSet = static_cast<SkAnalyticCubicEdge*>(currE)->setCubic(pts, sortY);
259 originalWinding = static_cast<SkAnalyticCubicEdge*>(currE)->fCEdge.fWinding;
Yuqian Lidf60e362017-07-25 11:26:31 -0400260 break;
261 }
262 }
263
264 if (!edgeSet) {
265 continue;
266 }
267
268 do {
269 currE->fX = currE->fUpperX;
270
271 SkFixed upperFloor = SkFixedFloorToFixed(currE->fUpperY);
272 SkFixed lowerCeil = SkFixedCeilToFixed(currE->fLowerY);
273 int iy = SkFixedFloorToInt(upperFloor);
274
275 if (lowerCeil <= upperFloor + SK_Fixed1) { // only one row is affected by the currE
276 SkFixed rowHeight = currE->fLowerY - currE->fUpperY;
277 SkFixed nextX = currE->fX + SkFixedMul(currE->fDX, rowHeight);
Yuqian Li8b0ef632018-03-08 13:39:27 -0500278 if (iy >= clippedIR.fTop && iy < clippedIR.fBottom) {
Yuqian Li5eb8fc52017-08-08 14:00:52 -0400279 add_coverage_delta_segment<true>(iy, rowHeight, currE, nextX, &result);
280 }
Yuqian Lidf60e362017-07-25 11:26:31 -0400281 continue;
282 }
283
284 // check first row
285 SkFixed rowHeight = upperFloor + SK_Fixed1 - currE->fUpperY;
286 SkFixed nextX;
287 if (rowHeight != SK_Fixed1) { // it's a partial row
288 nextX = currE->fX + SkFixedMul(currE->fDX, rowHeight);
289 add_coverage_delta_segment<true>(iy, rowHeight, currE, nextX, &result);
290 } else { // it's a full row so we can leave it to the while loop
291 iy--; // compensate the iy++ in the while loop
292 nextX = currE->fX;
293 }
294
295 while (true) { // process the full rows in the middle
296 iy++;
297 SkFixed y = SkIntToFixed(iy);
298 currE->fX = nextX;
299 nextX += currE->fDX;
300
301 if (y + SK_Fixed1 > currE->fLowerY) {
302 break; // no full rows left, break
303 }
304
305 // Check whether we're in the rect part that will be covered by blitAntiRect
306 if (iy >= rectTop && iy < rectBot) {
307 SkASSERT(currE->fDX == 0); // If yes, we must be on an edge with fDX = 0.
308 iy = rectBot - 1; // Skip the rect part by advancing iy to the bottom.
309 continue;
310 }
311
312 // Add current edge's coverage deltas on this full row
313 add_coverage_delta_segment<false>(iy, SK_Fixed1, currE, nextX, &result);
314 }
315
316 // last partial row
Yuqian Li3a5e1fe2017-09-28 10:58:38 -0400317 if (SkIntToFixed(iy) < currE->fLowerY &&
Yuqian Li8b0ef632018-03-08 13:39:27 -0500318 iy >= clippedIR.fTop && iy < clippedIR.fBottom) {
Yuqian Lidf60e362017-07-25 11:26:31 -0400319 rowHeight = currE->fLowerY - SkIntToFixed(iy);
320 nextX = currE->fX + SkFixedMul(currE->fDX, rowHeight);
321 add_coverage_delta_segment<true>(iy, rowHeight, currE, nextX, &result);
322 }
Yuqian Li5eb8fc52017-08-08 14:00:52 -0400323 // Intended assignment to fWinding to restore the maybe-negated winding (during updateLine)
324 } while ((currE->fWinding = originalWinding) && currE->update(currE->fLowerY, sortY));
Yuqian Lidf60e362017-07-25 11:26:31 -0400325 }
326}
327
Yuqian Lib49d7b02017-11-06 16:21:06 -0500328void SkScan::DAAFillPath(const SkPath& path, SkBlitter* blitter, const SkIRect& ir,
Yuqian Li36fa0ac2018-02-12 17:02:30 +0800329 const SkIRect& clipBounds, bool forceRLE, SkDAARecord* record) {
Yuqian Lif4848822018-01-08 13:40:45 -0500330 bool containedInClip = clipBounds.contains(ir);
Yuqian Lib49d7b02017-11-06 16:21:06 -0500331 bool isEvenOdd = path.getFillType() & 1;
332 bool isConvex = path.isConvex();
333 bool isInverse = path.isInverseFillType();
334 bool skipRect = isConvex && !isInverse;
Yuqian Li36fa0ac2018-02-12 17:02:30 +0800335 bool isInitOnce = record && record->fType == SkDAARecord::Type::kToBeComputed;
Yuqian Lidf60e362017-07-25 11:26:31 -0400336
Yuqian Lib49d7b02017-11-06 16:21:06 -0500337 SkIRect clippedIR = ir;
338 clippedIR.intersect(clipBounds);
Yuqian Lidf60e362017-07-25 11:26:31 -0400339
Yuqian Lib49d7b02017-11-06 16:21:06 -0500340 // The overhead of even constructing SkCoverageDeltaList/Mask is too big.
341 // So TryBlitFatAntiRect and return if it's successful.
342 if (!isInverse && TryBlitFatAntiRect(blitter, path, clipBounds)) {
Yuqian Lia445fdd2018-03-01 16:53:27 -0800343 SkDAARecord::SetEmpty(record);
Yuqian Lib49d7b02017-11-06 16:21:06 -0500344 return;
345 }
Yuqian Lidf60e362017-07-25 11:26:31 -0400346
Mike Klein6613cc52017-12-19 09:09:33 -0500347#ifdef SK_BUILD_FOR_GOOGLE3
Yuqian Lib49d7b02017-11-06 16:21:06 -0500348 constexpr int STACK_SIZE = 12 << 10; // 12K stack size alloc; Google3 has 16K limit.
Yuqian Libd40a5b2017-09-01 15:38:45 -0400349#else
Yuqian Lib49d7b02017-11-06 16:21:06 -0500350 constexpr int STACK_SIZE = 64 << 10; // 64k stack size to avoid heap allocation
Yuqian Libd40a5b2017-09-01 15:38:45 -0400351#endif
Yuqian Li36fa0ac2018-02-12 17:02:30 +0800352 SkSTArenaAlloc<STACK_SIZE> stackAlloc; // avoid heap allocation with SkSTArenaAlloc
Yuqian Libd40a5b2017-09-01 15:38:45 -0400353
Yuqian Li36fa0ac2018-02-12 17:02:30 +0800354 // Set alloc to record's alloc if and only if we're in the init-once phase. We have to do that
355 // during init phase because the mask or list needs to live longer. We can't do that during blit
356 // phase because the same record could be accessed by multiple threads simultaneously.
357 SkArenaAlloc* alloc = isInitOnce ? record->fAlloc : &stackAlloc;
358
359 if (record == nullptr) {
360 record = alloc->make<SkDAARecord>(alloc);
361 }
362
363 // Only blitter->blitXXX needs to be done in order in the threaded backend. Everything else can
364 // be done out of order in the init-once phase. We do that by calling DAAFillPath twice: first
365 // with a null blitter, and then second with the real blitter and the SkMask/SkCoverageDeltaList
366 // generated in the first step.
367 if (record->fType == SkDAARecord::Type::kToBeComputed) {
368 if (!forceRLE && !isInverse && SkCoverageDeltaMask::Suitable(clippedIR)) {
369 record->fType = SkDAARecord::Type::kMask;
370 SkCoverageDeltaMask deltaMask(alloc, clippedIR);
Yuqian Li8b0ef632018-03-08 13:39:27 -0500371 gen_alpha_deltas(path, clippedIR, clipBounds, deltaMask, blitter, skipRect,
372 containedInClip);
Yuqian Li36fa0ac2018-02-12 17:02:30 +0800373 deltaMask.convertCoverageToAlpha(isEvenOdd, isInverse, isConvex);
374 record->fMask = deltaMask.prepareSkMask();
375 } else {
376 record->fType = SkDAARecord::Type::kList;
377 SkCoverageDeltaList* deltaList = alloc->make<SkCoverageDeltaList>(
Yuqian Li575c21b2018-04-13 14:11:30 -0400378 alloc, clippedIR, forceRLE);
Yuqian Li8b0ef632018-03-08 13:39:27 -0500379 gen_alpha_deltas(path, clippedIR, clipBounds, *deltaList, blitter, skipRect,
380 containedInClip);
Yuqian Li36fa0ac2018-02-12 17:02:30 +0800381 record->fList = deltaList;
382 }
383 }
384
385 if (!isInitOnce) {
386 SkASSERT(record->fType != SkDAARecord::Type::kToBeComputed);
387 if (record->fType == SkDAARecord::Type::kMask) {
388 blitter->blitMask(record->fMask, clippedIR);
389 } else {
Yuqian Li94bb0722018-04-11 17:09:12 -0400390 blitter->blitCoverageDeltas(record->fList, clipBounds, isEvenOdd, isInverse, isConvex);
Yuqian Li36fa0ac2018-02-12 17:02:30 +0800391 }
Yuqian Lib49d7b02017-11-06 16:21:06 -0500392 }
Yuqian Lidf60e362017-07-25 11:26:31 -0400393}
Kevin Lubickf20c3492018-10-17 09:46:00 -0400394#endif //defined(SK_DISABLE_DAA)