• 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 "SkMaskBlurFilter.h"
9 
10 #include <cmath>
11 
12 #include "SkMakeUnique.h"
13 
14 static const double kPi = 3.14159265358979323846264338327950288;
15 
weight_from_diameter(uint32_t d)16 static uint64_t weight_from_diameter(uint32_t d) {
17     uint64_t d2 = d * d;
18     uint64_t d3 = d2 * d;
19     if ((d&1) == 0) {
20         // d * d * (d + 1);
21         return d3 + d2;
22     }
23 
24     return d3;
25 }
26 
filter_window(double sigma)27 static uint32_t filter_window(double sigma) {
28     auto possibleWindow = static_cast<uint32_t>(floor(sigma * 3 * sqrt(2*kPi)/4 + 0.5));
29     return std::max(1u, possibleWindow);
30 }
31 
FilterInfo(double sigma)32 SkMaskBlurFilter::FilterInfo::FilterInfo(double sigma)
33     : fFilterWindow{filter_window(sigma)}
34     , fScaledWeight{(static_cast<uint64_t>(1) << 32) / weight_from_diameter(fFilterWindow)} {}
35 
weight() const36 uint64_t SkMaskBlurFilter::FilterInfo::weight() const {
37     return weight_from_diameter(fFilterWindow);
38 
39 }
borderSize() const40 uint32_t SkMaskBlurFilter::FilterInfo::borderSize() const {
41     if ((fFilterWindow&1) == 0) {
42         return 3 * (fFilterWindow / 2) - 1;
43     }
44     return 3 * (fFilterWindow / 2);
45 }
46 
diameter(uint8_t pass) const47 size_t SkMaskBlurFilter::FilterInfo::diameter(uint8_t pass) const {
48     SkASSERT(pass <= 2);
49 
50     if ((fFilterWindow&1) == 0) {
51         // Handle even case.
52         switch (pass) {
53             case 0: return fFilterWindow;
54             case 1: return fFilterWindow;
55             case 2: return fFilterWindow+1;
56         }
57     }
58 
59     return fFilterWindow;
60 }
61 
scaledWeight() const62 uint64_t SkMaskBlurFilter::FilterInfo::scaledWeight() const {
63     return fScaledWeight;
64 }
65 
SkMaskBlurFilter(double sigmaW,double sigmaH)66 SkMaskBlurFilter::SkMaskBlurFilter(double sigmaW, double sigmaH)
67     : fInfoW{sigmaW}, fInfoH{sigmaH}
68     , fBuffer0{skstd::make_unique_default<uint32_t[]>(bufferSize(0))}
69     , fBuffer1{skstd::make_unique_default<uint32_t[]>(bufferSize(1))}
70     , fBuffer2{skstd::make_unique_default<uint32_t[]>(bufferSize(2))} {
71 }
72 
blur(const SkMask & src,SkMask * dst) const73 SkIPoint SkMaskBlurFilter::blur(const SkMask& src, SkMask* dst) const {
74 
75     uint64_t weightW = fInfoW.weight();
76     uint64_t weightH = fInfoH.weight();
77 
78     size_t borderW = fInfoW.borderSize();
79     size_t borderH = fInfoH.borderSize();
80 
81     size_t srcW = src.fBounds.width();
82     size_t srcH = src.fBounds.height();
83 
84     size_t dstW = srcW + 2 * borderW;
85     size_t dstH = srcH + 2 * borderH;
86 
87     dst->fBounds.set(0, 0, dstW, dstH);
88     dst->fBounds.offset(src.fBounds.x(), src.fBounds.y());
89     dst->fBounds.offset(-SkTo<int32_t>(borderW), -SkTo<int32_t>(borderH));
90 
91     dst->fImage = nullptr;
92     dst->fRowBytes = dstW;
93     dst->fFormat = SkMask::kA8_Format;
94 
95     if (src.fImage == nullptr) {
96         return {SkTo<int32_t>(borderW), SkTo<int32_t>(borderH)};
97     }
98 
99     dst->fImage = SkMask::AllocImage(dstW * dstH);
100 
101     if (weightW > 1 && weightH > 1) {
102         // Blur both directions.
103         size_t tmpW = srcH;
104         size_t tmpH = dstW;
105         auto tmp = skstd::make_unique_default<uint8_t[]>(tmpW * tmpH);
106 
107         // Blur horizontally, and transpose.
108         for (size_t y = 0; y < srcH; y++) {
109             auto srcStart = &src.fImage[y * src.fRowBytes];
110             auto tmpStart = &tmp[y];
111             this->blurOneScan(fInfoW,
112                               srcStart, 1, srcStart + srcW,
113                               tmpStart, tmpW, tmpStart + tmpW * tmpH);
114         }
115 
116         // Blur vertically (scan in memory order because of the transposition),
117         // and transpose back to the original orientation.
118         for (size_t y = 0; y < tmpH; y++) {
119             auto tmpStart = &tmp[y * tmpW];
120             auto dstStart = &dst->fImage[y];
121             this->blurOneScan(fInfoH,
122                               tmpStart, 1, tmpStart + tmpW,
123                               dstStart, dst->fRowBytes, dstStart + dst->fRowBytes * dstH);
124         }
125     } else if (weightW > 1) {
126         // Blur only horizontally.
127 
128         for (size_t y = 0; y < srcH; y++) {
129             auto srcStart = &src.fImage[y * src.fRowBytes];
130             auto dstStart = &dst->fImage[y * dst->fRowBytes];
131             this->blurOneScan(fInfoW,
132                               srcStart, 1, srcStart + srcW,
133                               dstStart, 1, dstStart + dstW);
134         }
135     } else if (weightH > 1) {
136         // Blur only vertically.
137 
138         for (size_t x = 0; x < srcW; x++) {
139             auto srcStart = &src.fImage[x];
140             auto srcEnd   = &src.fImage[src.fRowBytes * srcH];
141             auto dstStart = &dst->fImage[x];
142             auto dstEnd   = &dst->fImage[dst->fRowBytes * dstH];
143             this->blurOneScan(fInfoH,
144                               srcStart, src.fRowBytes, srcEnd,
145                               dstStart, dst->fRowBytes, dstEnd);
146         }
147     } else {
148         // Copy to dst. No Blur.
149 
150         for (size_t y = 0; y < srcH; y++) {
151             std::memcpy(&dst->fImage[y * dst->fRowBytes], &src.fImage[y * src.fRowBytes], dstW);
152         }
153     }
154 
155     return {SkTo<int32_t>(borderW), SkTo<int32_t>(borderH)};
156 }
157 
bufferSize(uint8_t bufferPass) const158 size_t SkMaskBlurFilter::bufferSize(uint8_t bufferPass) const {
159     return std::max(fInfoW.diameter(bufferPass), fInfoH.diameter(bufferPass)) - 1;
160 }
161 
162 // Blur one horizontal scan into the dst.
blurOneScan(FilterInfo info,const uint8_t * src,size_t srcStride,const uint8_t * srcEnd,uint8_t * dst,size_t dstStride,uint8_t * dstEnd) const163 void SkMaskBlurFilter::blurOneScan(
164     FilterInfo info,
165     const uint8_t* src, size_t srcStride, const uint8_t* srcEnd,
166           uint8_t* dst, size_t dstStride,       uint8_t* dstEnd) const {
167 
168     auto buffer0Begin = &fBuffer0[0];
169     auto buffer1Begin = &fBuffer1[0];
170     auto buffer2Begin = &fBuffer2[0];
171 
172     auto buffer0Cursor = buffer0Begin;
173     auto buffer1Cursor = buffer1Begin;
174     auto buffer2Cursor = buffer2Begin;
175 
176     auto buffer0End = &fBuffer0[0] + info.diameter(0) - 1;
177     auto buffer1End = &fBuffer1[0] + info.diameter(1) - 1;
178     auto buffer2End = &fBuffer2[0] + info.diameter(2) - 1;
179 
180     std::memset(&fBuffer0[0], 0, (buffer0End - buffer0Begin) * sizeof(fBuffer0[0]));
181     std::memset(&fBuffer1[0], 0, (buffer1End - buffer1Begin) * sizeof(fBuffer1[0]));
182     std::memset(&fBuffer2[0], 0, (buffer2End - buffer2Begin) * sizeof(fBuffer2[0]));
183 
184     uint32_t sum0 = 0;
185     uint32_t sum1 = 0;
186     uint32_t sum2 = 0;
187 
188     const uint64_t half = static_cast<uint64_t>(1) << 31;
189 
190     // Consume the source generating pixels.
191     for (auto srcCursor = src; srcCursor < srcEnd; dst += dstStride, srcCursor += srcStride) {
192         uint32_t s = *srcCursor;
193         sum0 += s;
194         sum1 += sum0;
195         sum2 += sum1;
196 
197         *dst = SkTo<uint8_t>((info.scaledWeight() * sum2 + half) >> 32);
198 
199         sum2 -= *buffer2Cursor;
200         *buffer2Cursor = sum1;
201         buffer2Cursor = (buffer2Cursor + 1) < buffer2End ? buffer2Cursor + 1 : &fBuffer2[0];
202 
203         sum1 -= *buffer1Cursor;
204         *buffer1Cursor = sum0;
205         buffer1Cursor = (buffer1Cursor + 1) < buffer1End ? buffer1Cursor + 1 : &fBuffer1[0];
206 
207         sum0 -= *buffer0Cursor;
208         *buffer0Cursor = s;
209         buffer0Cursor = (buffer0Cursor + 1) < buffer0End ? buffer0Cursor + 1 : &fBuffer0[0];
210     }
211 
212     // This handles the case when both ends of the box are not between [src, srcEnd), and both
213     // are zero at that point.
214     for (auto i = 0; i < static_cast<ptrdiff_t>(2 * info.borderSize()) - (srcEnd - src); i++) {
215         uint32_t s = 0;
216         sum0 += s;
217         sum1 += sum0;
218         sum2 += sum1;
219 
220         *dst = SkTo<uint8_t>((info.scaledWeight() * sum2 + half) >> 32);
221 
222         sum2 -= *buffer2Cursor;
223         *buffer2Cursor = sum1;
224         buffer2Cursor = (buffer2Cursor + 1) < buffer2End ? buffer2Cursor + 1 : &fBuffer2[0];
225 
226         sum1 -= *buffer1Cursor;
227         *buffer1Cursor = sum0;
228         buffer1Cursor = (buffer1Cursor + 1) < buffer1End ? buffer1Cursor + 1 : &fBuffer1[0];
229 
230         sum0 -= *buffer0Cursor;
231         *buffer0Cursor = s;
232         buffer0Cursor = (buffer0Cursor + 1) < buffer0End ? buffer0Cursor + 1 : &fBuffer0[0];
233         dst += dstStride;
234     }
235 
236     // Starting from the right, fill in the rest of the buffer.
237     std::memset(&fBuffer0[0], 0, (buffer0End - &fBuffer0[0]) * sizeof(fBuffer0[0]));
238     std::memset(&fBuffer1[0], 0, (buffer1End - &fBuffer1[0]) * sizeof(fBuffer1[0]));
239     std::memset(&fBuffer2[0], 0, (buffer2End - &fBuffer2[0]) * sizeof(fBuffer2[0]));
240 
241     sum0 = sum1 = sum2 = 0;
242 
243     uint8_t* dstCursor = dstEnd;
244     const uint8_t* srcCursor = srcEnd;
245     do {
246         dstCursor -= dstStride;
247         srcCursor -= srcStride;
248         uint32_t s = *srcCursor;
249         sum0 += s;
250         sum1 += sum0;
251         sum2 += sum1;
252 
253         *dstCursor = SkTo<uint8_t>((info.scaledWeight() * sum2 + half) >> 32);
254 
255         sum2 -= *buffer2Cursor;
256         *buffer2Cursor = sum1;
257         buffer2Cursor = (buffer2Cursor + 1) < buffer2End ? buffer2Cursor + 1 : &fBuffer2[0];
258 
259         sum1 -= *buffer1Cursor;
260         *buffer1Cursor = sum0;
261         buffer1Cursor = (buffer1Cursor + 1) < buffer1End ? buffer1Cursor + 1 : &fBuffer1[0];
262 
263         sum0 -= *buffer0Cursor;
264         *buffer0Cursor = s;
265         buffer0Cursor = (buffer0Cursor + 1) < buffer0End ? buffer0Cursor + 1 : &fBuffer0[0];
266     } while (dstCursor > dst);
267 }
268