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