• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 Google Inc.
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 "SkCoverageDelta.h"
9 
SkCoverageDeltaList(SkArenaAlloc * alloc,const SkIRect & bounds,bool forceRLE)10 SkCoverageDeltaList::SkCoverageDeltaList(SkArenaAlloc* alloc, const SkIRect& bounds, bool forceRLE) {
11     fAlloc              = alloc;
12     fBounds             = bounds;
13     fForceRLE           = forceRLE;
14 
15     int top             = bounds.fTop;
16     int bottom          = bounds.fBottom;
17 
18     // Init the anti-rect to be empty
19     fAntiRect.fY        = bottom;
20     fAntiRect.fHeight   = 0;
21 
22     fSorted     = fAlloc->makeArrayDefault<bool>(bottom - top);
23     fCounts     = fAlloc->makeArrayDefault<int>((bottom - top) * 2);
24     fMaxCounts  = fCounts + bottom - top;
25     fRows       = fAlloc->makeArrayDefault<SkCoverageDelta*>(bottom - top) - top;
26     fRows[top]  = fAlloc->makeArrayDefault<SkCoverageDelta>(INIT_ROW_SIZE * (bottom - top));
27 
28     memset(fSorted, true, bottom - top);
29     memset(fCounts, 0, sizeof(int) * (bottom - top));
30 
31     // Minus top so we can directly use fCounts[y] instead of fCounts[y - fTop].
32     // Same for fMaxCounts, fRows, and fSorted.
33     fSorted    -= top;
34     fCounts    -= top;
35     fMaxCounts -= top;
36 
37     for(int y = top; y < bottom; ++y) {
38         fMaxCounts[y] = INIT_ROW_SIZE;
39     }
40     for(int y = top + 1; y < bottom; ++y) {
41         fRows[y] = fRows[y - 1] + INIT_ROW_SIZE;
42     }
43 }
44 
ExpandWidth(int width)45 int SkCoverageDeltaMask::ExpandWidth(int width) {
46     int result = width + PADDING * 2;
47     return result + (SIMD_WIDTH - result % SIMD_WIDTH) % SIMD_WIDTH;
48 }
49 
CanHandle(const SkIRect & bounds)50 bool SkCoverageDeltaMask::CanHandle(const SkIRect& bounds) {
51     // Return early if either width or height is very large because width * height might overflow.
52     if (bounds.width() >= MAX_MASK_SIZE || bounds.height() >= MAX_MASK_SIZE) {
53         return false;
54     }
55     // Expand width so we don't have to worry about the boundary
56     return ExpandWidth(bounds.width()) * bounds.height() + PADDING * 2 < MAX_MASK_SIZE;
57 }
58 
Suitable(const SkIRect & bounds)59 bool SkCoverageDeltaMask::Suitable(const SkIRect& bounds) {
60     return bounds.width() <= SUITABLE_WIDTH && CanHandle(bounds);
61 }
62 
SkCoverageDeltaMask(SkArenaAlloc * alloc,const SkIRect & bounds)63 SkCoverageDeltaMask::SkCoverageDeltaMask(SkArenaAlloc* alloc, const SkIRect& bounds) {
64     SkASSERT(CanHandle(bounds));
65 
66     fBounds             = bounds;
67 
68     // Init the anti-rect to be empty
69     fAntiRect.fY        = fBounds.fBottom;
70     fAntiRect.fHeight   = 0;
71 
72     fExpandedWidth      = ExpandWidth(fBounds.width());
73 
74     int size            = fExpandedWidth * bounds.height() + PADDING * 2;
75     fDeltaStorage       = alloc->makeArray<SkFixed>(size);
76     fMask               = alloc->makeArrayDefault<SkAlpha>(size);
77 
78     // Add PADDING columns so we may access fDeltas[index(-PADDING, 0)]
79     // Minus index(fBounds.fLeft, fBounds.fTop) so we can directly access fDeltas[index(x, y)]
80     fDeltas             = fDeltaStorage + PADDING - this->index(fBounds.fLeft, fBounds.fTop);
81 }
82 
83 // TODO As this function is so performance-critical (and we're thinking so much about SIMD), use
84 // SkOpts framework to compile multiple versions of this function so we can choose the best one
85 // available at runtime.
convertCoverageToAlpha(bool isEvenOdd,bool isInverse,bool isConvex)86 void SkCoverageDeltaMask::convertCoverageToAlpha(bool isEvenOdd, bool isInverse, bool isConvex) {
87     SkFixed* deltaRow = &this->delta(fBounds.fLeft, fBounds.fTop);
88     SkAlpha* maskRow = fMask;
89     for(int iy = 0; iy < fBounds.height(); ++iy) {
90         // If we're inside fAntiRect, blit it to the mask and advance to its bottom
91         if (fAntiRect.fHeight && iy == fAntiRect.fY - fBounds.fTop) {
92             // Blit the mask
93             int L = fAntiRect.fX - fBounds.fLeft;
94             for(int i = 0; i < fAntiRect.fHeight; ++i) {
95                 sk_bzero(maskRow, fBounds.width());
96                 SkAlpha* tMask = maskRow + L;
97                 if (fAntiRect.fLeftAlpha) {
98                     tMask[0] = fAntiRect.fLeftAlpha;
99                 }
100                 memset(tMask + 1, 0xff, fAntiRect.fWidth);
101                 if (fAntiRect.fRightAlpha) {
102                     tMask[fAntiRect.fWidth + 1] = fAntiRect.fRightAlpha;
103                 }
104                 maskRow += fBounds.width();
105             }
106 
107             // Advance to the bottom (maskRow is already advanced to the bottom).
108             deltaRow    += fExpandedWidth * fAntiRect.fHeight;
109             iy          += fAntiRect.fHeight - 1; // -1 because we'll ++iy after continue
110             continue;
111         }
112 
113         // Otherwise, cumulate deltas into coverages, and convert them into alphas
114         SkFixed c[SIMD_WIDTH] = {0}; // prepare SIMD_WIDTH coverages at a time
115         for(int ix = 0; ix < fExpandedWidth; ix += SIMD_WIDTH) {
116             // Future todo: is it faster to process SIMD_WIDTH rows at a time so we can use SIMD
117             // for coverage accumulation?
118 
119             // Cumulate deltas to get SIMD_WIDTH new coverages
120             c[0] = c[SIMD_WIDTH - 1] + deltaRow[ix];
121             for(int j = 1; j < SIMD_WIDTH; ++j) {
122                 c[j] = c[j - 1] + deltaRow[ix + j];
123             }
124 
125             using SkNi = SkNx<SIMD_WIDTH, int>;
126             SkNi cn = SkNi::Load(c);
127             SkNi an = isConvex ? ConvexCoverageToAlpha(cn, isInverse)
128                                : CoverageToAlpha(cn, isEvenOdd, isInverse);
129             SkNx_cast<SkAlpha>(an).store(maskRow + ix);
130         }
131 
132         // Finally, advance to the next row
133         deltaRow    += fExpandedWidth;
134         maskRow     += fBounds.width();
135     }
136 }
137