• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2025 Google LLC
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 #include "src/core/SkMaskFilterBase.h"
8 
9 #include "include/core/SkImageFilter.h"
10 #include "include/core/SkMatrix.h"
11 #include "include/core/SkPath.h"
12 #include "include/core/SkPoint.h"
13 #include "include/core/SkRect.h"
14 #include "include/core/SkRefCnt.h"
15 #include "include/core/SkRegion.h"
16 #include "include/core/SkStrokeRec.h"
17 #include "include/core/SkTypes.h"
18 #include "include/private/base/SkTemplates.h"
19 #include "src/base/SkAutoMalloc.h"
20 #include "src/core/SkBlitter.h"
21 #include "src/core/SkCachedData.h"
22 #include "src/core/SkDraw.h"
23 #include "src/core/SkMask.h"
24 #include "src/core/SkPathPriv.h"
25 #include "src/core/SkRasterClip.h"
26 
27 #include <algorithm>
28 #include <cstddef>
29 #include <cstdint>
30 #include <optional>
31 
32 class SkRRect;
33 
~NinePatch()34 SkMaskFilterBase::NinePatch::~NinePatch() {
35     if (fCache) {
36         SkASSERT((const void*)fMask.fImage == fCache->data());
37         fCache->unref();
38     } else {
39         // fMask is about to be destroyed and "owns" its fImage.
40         SkMaskBuilder::FreeImage(const_cast<uint8_t*>(fMask.fImage));
41     }
42 }
43 
asABlur(BlurRec *) const44 bool SkMaskFilterBase::asABlur(BlurRec*) const {
45     return false;
46 }
47 
asImageFilter(const SkMatrix & ctm) const48 sk_sp<SkImageFilter> SkMaskFilterBase::asImageFilter(const SkMatrix& ctm) const {
49     return nullptr;
50 }
51 
extract_mask_subset(const SkMask & src,SkIRect bounds,int32_t newX,int32_t newY)52 static SkMask extract_mask_subset(const SkMask& src, SkIRect bounds, int32_t newX, int32_t newY) {
53     SkASSERT(src.fBounds.contains(bounds));
54 
55     const int dx = bounds.left() - src.fBounds.left();
56     const int dy = bounds.top() - src.fBounds.top();
57     bounds.offsetTo(newX, newY);
58     return SkMask(src.fImage + dy * src.fRowBytes + dx,
59                   bounds,
60                   src.fRowBytes,
61                   src.fFormat);
62 }
63 
blit_clipped_mask(SkBlitter * blitter,const SkMask & mask,const SkIRect & bounds,const SkIRect & clipR)64 static void blit_clipped_mask(SkBlitter* blitter, const SkMask& mask,
65                             const SkIRect& bounds, const SkIRect& clipR) {
66     SkIRect r;
67     if (r.intersect(bounds, clipR)) {
68         blitter->blitMask(mask, r);
69     }
70 }
71 
blit_clipped_rect(SkBlitter * blitter,const SkIRect & rect,const SkIRect & clipR)72 static void blit_clipped_rect(SkBlitter* blitter, const SkIRect& rect, const SkIRect& clipR) {
73     SkIRect r;
74     if (r.intersect(rect, clipR)) {
75         blitter->blitRect(r.left(), r.top(), r.width(), r.height());
76     }
77 }
78 
draw_nine_clipped(const SkMask & mask,const SkIRect & outerR,const SkIPoint & center,bool fillCenter,const SkIRect & clipR,SkBlitter * blitter)79 static void draw_nine_clipped(const SkMask& mask, const SkIRect& outerR,
80                               const SkIPoint& center, bool fillCenter,
81                               const SkIRect& clipR, SkBlitter* blitter) {
82     const int cx = center.x();
83     const int cy = center.y();
84     SkIRect bounds;
85 
86     // top-left
87     bounds = mask.fBounds;
88     bounds.fRight = cx;
89     bounds.fBottom = cy;
90     if (bounds.width() > 0 && bounds.height() > 0) {
91         SkMask m = extract_mask_subset(mask, bounds, outerR.left(), outerR.top());
92         blit_clipped_mask(blitter, m, m.fBounds, clipR);
93     }
94 
95     // top-right
96     bounds = mask.fBounds;
97     bounds.fLeft = cx + 1;
98     bounds.fBottom = cy;
99     if (bounds.width() > 0 && bounds.height() > 0) {
100         SkMask m = extract_mask_subset(mask, bounds, outerR.right() - bounds.width(), outerR.top());
101         blit_clipped_mask(blitter, m, m.fBounds, clipR);
102     }
103 
104     // bottom-left
105     bounds = mask.fBounds;
106     bounds.fRight = cx;
107     bounds.fTop = cy + 1;
108     if (bounds.width() > 0 && bounds.height() > 0) {
109         SkMask m = extract_mask_subset(mask, bounds, outerR.left(), outerR.bottom() - bounds.height());
110         blit_clipped_mask(blitter, m, m.fBounds, clipR);
111     }
112 
113     // bottom-right
114     bounds = mask.fBounds;
115     bounds.fLeft = cx + 1;
116     bounds.fTop = cy + 1;
117     if (bounds.width() > 0 && bounds.height() > 0) {
118         SkMask m = extract_mask_subset(mask, bounds, outerR.right() - bounds.width(),
119                                                    outerR.bottom() - bounds.height());
120         blit_clipped_mask(blitter, m, m.fBounds, clipR);
121     }
122 
123     SkIRect innerR;
124     innerR.setLTRB(outerR.left() + cx - mask.fBounds.left(),
125                    outerR.top() + cy - mask.fBounds.top(),
126                    outerR.right() + (cx + 1 - mask.fBounds.right()),
127                    outerR.bottom() + (cy + 1 - mask.fBounds.bottom()));
128     if (fillCenter) {
129         blit_clipped_rect(blitter, innerR, clipR);
130     }
131 
132     const int innerW = innerR.width();
133     size_t storageSize = (innerW + 1) * (sizeof(int16_t) + sizeof(uint8_t));
134     SkAutoSMalloc<4*1024> storage(storageSize);
135     int16_t* runs = (int16_t*)storage.get();
136     uint8_t* alpha = (uint8_t*)(runs + innerW + 1);
137 
138     SkIRect r;
139     // top
140     r.setLTRB(innerR.left(), outerR.top(), innerR.right(), innerR.top());
141     if (r.intersect(clipR)) {
142         const int startY = std::max(0, r.top() - outerR.top());
143         const int stopY = startY + r.height();
144         const int width = r.width();
145         runs[0] = width;
146         runs[width] = 0;
147         for (int y = startY; y < stopY; ++y) {
148             alpha[0] = *mask.getAddr8(cx, mask.fBounds.top() + y);
149             blitter->blitAntiH(r.left(), outerR.top() + y, alpha, runs);
150         }
151     }
152     // bottom
153     r.setLTRB(innerR.left(), innerR.bottom(), innerR.right(), outerR.bottom());
154     if (r.intersect(clipR)) {
155         const int startY = outerR.bottom() - r.bottom();
156         const int stopY = startY + r.height();
157         const int width = r.width();
158         runs[0] = width;
159         runs[width] = 0;
160         for (int y = startY; y < stopY; ++y) {
161             alpha[0] = *mask.getAddr8(cx, mask.fBounds.bottom() - y - 1);
162             blitter->blitAntiH(r.left(), outerR.bottom() - y - 1, alpha, runs);
163         }
164     }
165     // left
166     r.setLTRB(outerR.left(), innerR.top(), innerR.left(), innerR.bottom());
167     if (r.intersect(clipR)) {
168         SkMask leftMask(mask.getAddr8(mask.fBounds.left() + r.left() - outerR.left(),
169                                       mask.fBounds.top() + cy),
170                         r,
171                         0,    // so we repeat the scanline for our height
172                         SkMask::kA8_Format);
173         blitter->blitMask(leftMask, r);
174     }
175     // right
176     r.setLTRB(innerR.right(), innerR.top(), outerR.right(), innerR.bottom());
177     if (r.intersect(clipR)) {
178         SkMask rightMask(mask.getAddr8(mask.fBounds.right() - outerR.right() + r.left(),
179                                        mask.fBounds.top() + cy),
180                          r,
181                          0,    // so we repeat the scanline for our height
182                          SkMask::kA8_Format);
183         blitter->blitMask(rightMask, r);
184     }
185 }
186 
draw_nine(const SkMask & mask,const SkIRect & outerR,const SkIPoint & center,bool fillCenter,const SkRasterClip & clip,SkBlitter * blitter)187 static void draw_nine(const SkMask& mask, const SkIRect& outerR, const SkIPoint& center,
188                       bool fillCenter, const SkRasterClip& clip, SkBlitter* blitter) {
189     // if we get here, we need to (possibly) resolve the clip and blitter
190     SkAAClipBlitterWrapper wrapper(clip, blitter);
191     blitter = wrapper.getBlitter();
192 
193     SkRegion::Cliperator clipper(wrapper.getRgn(), outerR);
194 
195     if (!clipper.done()) {
196         const SkIRect& cr = clipper.rect();
197         do {
198             draw_nine_clipped(mask, outerR, center, fillCenter, cr, blitter);
199             clipper.next();
200         } while (!clipper.done());
201     }
202 }
203 
countNestedRects(const SkPath & path,SkRect rects[2])204 static int countNestedRects(const SkPath& path, SkRect rects[2]) {
205     if (SkPathPriv::IsNestedFillRects(path, rects)) {
206         return 2;
207     }
208     return path.isRect(&rects[0]);
209 }
210 
filterRRect(const SkRRect & devRRect,const SkMatrix & matrix,const SkRasterClip & clip,SkBlitter * blitter) const211 bool SkMaskFilterBase::filterRRect(const SkRRect& devRRect, const SkMatrix& matrix,
212                                    const SkRasterClip& clip, SkBlitter* blitter) const {
213     // Attempt to speed up drawing by creating a nine patch. If a nine patch
214     // cannot be used, return false to allow our caller to recover and perform
215     // the drawing another way.
216     std::optional<NinePatch> patch = this->filterRRectToNine(devRRect, matrix, clip.getBounds());
217 
218     if (!patch.has_value()) {
219         return false;
220     }
221     draw_nine(patch->fMask, patch->fOuterRect, patch->fCenter, true, clip, blitter);
222     return true;
223 }
224 
filterPath(const SkPath & devPath,const SkMatrix & matrix,const SkRasterClip & clip,SkBlitter * blitter,SkStrokeRec::InitStyle style) const225 bool SkMaskFilterBase::filterPath(const SkPath& devPath, const SkMatrix& matrix,
226                                   const SkRasterClip& clip, SkBlitter* blitter,
227                                   SkStrokeRec::InitStyle style) const {
228     SkRect rects[2];
229     int rectCount = 0;
230     if (SkStrokeRec::kFill_InitStyle == style) {
231         rectCount = countNestedRects(devPath, rects);
232     }
233     if (rectCount > 0) {
234         std::optional<NinePatch> patch;
235 
236         switch (this->filterRectsToNine(
237                 SkSpan(rects, rectCount), matrix, clip.getBounds(), &patch)) {
238             case FilterReturn::kFalse:
239                 SkASSERT(!patch.has_value());
240                 return false;
241 
242             case FilterReturn::kTrue:
243                 draw_nine(patch->fMask, patch->fOuterRect, patch->fCenter, 1 == rectCount, clip,
244                           blitter);
245                 return true;
246 
247             case FilterReturn::kUnimplemented:
248                 SkASSERT(!patch.has_value());
249                 // fall out
250                 break;
251         }
252     }
253 
254     SkMaskBuilder srcM, dstM;
255 
256 #if defined(SK_BUILD_FOR_FUZZER)
257     if (devPath.countVerbs() > 1000 || devPath.countPoints() > 1000) {
258         return false;
259     }
260 #endif
261     if (!SkDraw::DrawToMask(devPath, clip.getBounds(), this, &matrix, &srcM,
262                             SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode,
263                             style)) {
264         return false;
265     }
266     SkAutoMaskFreeImage autoSrc(srcM.image());
267 
268     if (!this->filterMask(&dstM, srcM, matrix, nullptr)) {
269         return false;
270     }
271     SkAutoMaskFreeImage autoDst(dstM.image());
272 
273     // if we get here, we need to (possibly) resolve the clip and blitter
274     SkAAClipBlitterWrapper wrapper(clip, blitter);
275     blitter = wrapper.getBlitter();
276 
277     SkRegion::Cliperator clipper(wrapper.getRgn(), dstM.fBounds);
278 
279     if (!clipper.done()) {
280         const SkIRect& cr = clipper.rect();
281         do {
282             blitter->blitMask(dstM, cr);
283             clipper.next();
284         } while (!clipper.done());
285     }
286 
287     return true;
288 }
289 
filterRRectToNine(const SkRRect &,const SkMatrix &,const SkIRect &) const290 std::optional<SkMaskFilterBase::NinePatch> SkMaskFilterBase::filterRRectToNine(
291         const SkRRect&, const SkMatrix&, const SkIRect&) const {
292     return std::nullopt;
293 }
294 
filterRectsToNine(SkSpan<const SkRect>,const SkMatrix &,const SkIRect &,std::optional<NinePatch> *) const295 SkMaskFilterBase::FilterReturn SkMaskFilterBase::filterRectsToNine(
296         SkSpan<const SkRect>,
297         const SkMatrix&,
298         const SkIRect&,
299         std::optional<NinePatch>*) const {
300     return FilterReturn::kUnimplemented;
301 }
302 
computeFastBounds(const SkRect & src,SkRect * dst) const303 void SkMaskFilterBase::computeFastBounds(const SkRect& src, SkRect* dst) const {
304     SkMask srcM(nullptr, src.roundOut(), 0, SkMask::kA8_Format);
305     SkMaskBuilder dstM;
306 
307     SkIPoint margin;    // ignored
308     if (this->filterMask(&dstM, srcM, SkMatrix::I(), &margin)) {
309         dst->set(dstM.fBounds);
310     } else {
311         dst->set(srcM.fBounds);
312     }
313 }
314