blob: bb700cd4b93fc81e4ca605d442c62bfb38572342 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "SkMaskFilter.h"
11#include "SkBlitter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkDraw.h"
reedb0df8be2015-02-04 09:07:17 -080013#include "SkCachedData.h"
reed@google.com045e62d2011-10-24 12:19:46 +000014#include "SkRasterClip.h"
scroggo@google.coma8e33a92013-11-08 18:02:53 +000015#include "SkRRect.h"
robertphillips@google.com49149312013-07-03 15:34:35 +000016#include "SkTypes.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000017
robertphillips@google.com0b828522013-07-03 15:49:05 +000018#if SK_SUPPORT_GPU
19#include "GrTexture.h"
20#include "SkGr.h"
21#include "SkGrPixelRef.h"
22#endif
reed@google.comd729b3e2012-11-09 14:30:48 +000023
reedb0df8be2015-02-04 09:07:17 -080024SkMaskFilter::NinePatch::~NinePatch() {
25 if (fCache) {
26 SkASSERT((const void*)fMask.fImage == fCache->data());
27 fCache->unref();
28 } else {
29 SkMask::FreeImage(fMask.fImage);
30 }
31}
32
reed@google.comfeb8cc82011-04-19 20:11:25 +000033bool SkMaskFilter::filterMask(SkMask*, const SkMask&, const SkMatrix&,
reed@google.com30711b72012-12-18 19:18:39 +000034 SkIPoint*) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +000035 return false;
36}
37
reed@google.comdaaafa62014-04-29 15:20:16 +000038bool SkMaskFilter::asABlur(BlurRec*) const {
39 return false;
40}
41
reed@google.comd729b3e2012-11-09 14:30:48 +000042static void extractMaskSubset(const SkMask& src, SkMask* dst) {
43 SkASSERT(src.fBounds.contains(dst->fBounds));
44
45 const int dx = dst->fBounds.left() - src.fBounds.left();
46 const int dy = dst->fBounds.top() - src.fBounds.top();
47 dst->fImage = src.fImage + dy * src.fRowBytes + dx;
48 dst->fRowBytes = src.fRowBytes;
49 dst->fFormat = src.fFormat;
50}
51
52static void blitClippedMask(SkBlitter* blitter, const SkMask& mask,
53 const SkIRect& bounds, const SkIRect& clipR) {
54 SkIRect r;
55 if (r.intersect(bounds, clipR)) {
56 blitter->blitMask(mask, r);
57 }
58}
59
60static void blitClippedRect(SkBlitter* blitter, const SkIRect& rect, const SkIRect& clipR) {
61 SkIRect r;
62 if (r.intersect(rect, clipR)) {
63 blitter->blitRect(r.left(), r.top(), r.width(), r.height());
64 }
65}
66
67#if 0
68static void dump(const SkMask& mask) {
69 for (int y = mask.fBounds.top(); y < mask.fBounds.bottom(); ++y) {
70 for (int x = mask.fBounds.left(); x < mask.fBounds.right(); ++x) {
71 SkDebugf("%02X", *mask.getAddr8(x, y));
72 }
73 SkDebugf("\n");
74 }
75 SkDebugf("\n");
76}
77#endif
78
79static void draw_nine_clipped(const SkMask& mask, const SkIRect& outerR,
reed@google.comdab9b4f2012-11-19 16:45:14 +000080 const SkIPoint& center, bool fillCenter,
reed@google.comd729b3e2012-11-09 14:30:48 +000081 const SkIRect& clipR, SkBlitter* blitter) {
reed@google.comdab9b4f2012-11-19 16:45:14 +000082 int cx = center.x();
83 int cy = center.y();
reed@google.comd729b3e2012-11-09 14:30:48 +000084 SkMask m;
skia.committer@gmail.com453995e2012-11-10 02:01:26 +000085
reed@google.comd729b3e2012-11-09 14:30:48 +000086 // top-left
87 m.fBounds = mask.fBounds;
88 m.fBounds.fRight = cx;
89 m.fBounds.fBottom = cy;
tomhudson@google.com3c0ecc52013-04-25 16:56:40 +000090 if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
91 extractMaskSubset(mask, &m);
92 m.fBounds.offsetTo(outerR.left(), outerR.top());
93 blitClippedMask(blitter, m, m.fBounds, clipR);
94 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +000095
reed@google.comd729b3e2012-11-09 14:30:48 +000096 // top-right
97 m.fBounds = mask.fBounds;
98 m.fBounds.fLeft = cx + 1;
99 m.fBounds.fBottom = cy;
tomhudson@google.com3c0ecc52013-04-25 16:56:40 +0000100 if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
101 extractMaskSubset(mask, &m);
102 m.fBounds.offsetTo(outerR.right() - m.fBounds.width(), outerR.top());
103 blitClippedMask(blitter, m, m.fBounds, clipR);
104 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +0000105
reed@google.comd729b3e2012-11-09 14:30:48 +0000106 // bottom-left
107 m.fBounds = mask.fBounds;
108 m.fBounds.fRight = cx;
109 m.fBounds.fTop = cy + 1;
tomhudson@google.com3c0ecc52013-04-25 16:56:40 +0000110 if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
111 extractMaskSubset(mask, &m);
112 m.fBounds.offsetTo(outerR.left(), outerR.bottom() - m.fBounds.height());
113 blitClippedMask(blitter, m, m.fBounds, clipR);
114 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +0000115
reed@google.comd729b3e2012-11-09 14:30:48 +0000116 // bottom-right
117 m.fBounds = mask.fBounds;
118 m.fBounds.fLeft = cx + 1;
119 m.fBounds.fTop = cy + 1;
tomhudson@google.com3c0ecc52013-04-25 16:56:40 +0000120 if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
121 extractMaskSubset(mask, &m);
122 m.fBounds.offsetTo(outerR.right() - m.fBounds.width(),
123 outerR.bottom() - m.fBounds.height());
124 blitClippedMask(blitter, m, m.fBounds, clipR);
125 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000126
127 SkIRect innerR;
128 innerR.set(outerR.left() + cx - mask.fBounds.left(),
129 outerR.top() + cy - mask.fBounds.top(),
130 outerR.right() + (cx + 1 - mask.fBounds.right()),
131 outerR.bottom() + (cy + 1 - mask.fBounds.bottom()));
reed@google.comdab9b4f2012-11-19 16:45:14 +0000132 if (fillCenter) {
133 blitClippedRect(blitter, innerR, clipR);
134 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000135
136 const int innerW = innerR.width();
137 size_t storageSize = (innerW + 1) * (sizeof(int16_t) + sizeof(uint8_t));
138 SkAutoSMalloc<4*1024> storage(storageSize);
139 int16_t* runs = (int16_t*)storage.get();
140 uint8_t* alpha = (uint8_t*)(runs + innerW + 1);
141
142 SkIRect r;
143 // top
144 r.set(innerR.left(), outerR.top(), innerR.right(), innerR.top());
145 if (r.intersect(clipR)) {
146 int startY = SkMax32(0, r.top() - outerR.top());
147 int stopY = startY + r.height();
148 int width = r.width();
149 for (int y = startY; y < stopY; ++y) {
150 runs[0] = width;
151 runs[width] = 0;
152 alpha[0] = *mask.getAddr8(cx, mask.fBounds.top() + y);
153 blitter->blitAntiH(r.left(), outerR.top() + y, alpha, runs);
154 }
155 }
156 // bottom
157 r.set(innerR.left(), innerR.bottom(), innerR.right(), outerR.bottom());
158 if (r.intersect(clipR)) {
159 int startY = outerR.bottom() - r.bottom();
160 int stopY = startY + r.height();
161 int width = r.width();
162 for (int y = startY; y < stopY; ++y) {
163 runs[0] = width;
164 runs[width] = 0;
165 alpha[0] = *mask.getAddr8(cx, mask.fBounds.bottom() - y - 1);
166 blitter->blitAntiH(r.left(), outerR.bottom() - y - 1, alpha, runs);
167 }
168 }
169 // left
170 r.set(outerR.left(), innerR.top(), innerR.left(), innerR.bottom());
171 if (r.intersect(clipR)) {
172 int startX = r.left() - outerR.left();
173 int stopX = startX + r.width();
174 int height = r.height();
175 for (int x = startX; x < stopX; ++x) {
176 blitter->blitV(outerR.left() + x, r.top(), height,
177 *mask.getAddr8(mask.fBounds.left() + x, mask.fBounds.top() + cy));
178 }
179 }
180 // right
181 r.set(innerR.right(), innerR.top(), outerR.right(), innerR.bottom());
182 if (r.intersect(clipR)) {
183 int startX = outerR.right() - r.right();
184 int stopX = startX + r.width();
185 int height = r.height();
186 for (int x = startX; x < stopX; ++x) {
187 blitter->blitV(outerR.right() - x - 1, r.top(), height,
188 *mask.getAddr8(mask.fBounds.right() - x - 1, mask.fBounds.top() + cy));
189 }
190 }
191}
192
reed868074b2014-06-03 10:53:59 -0700193static void draw_nine(const SkMask& mask, const SkIRect& outerR, const SkIPoint& center,
194 bool fillCenter, const SkRasterClip& clip, SkBlitter* blitter) {
reed@google.comd729b3e2012-11-09 14:30:48 +0000195 // if we get here, we need to (possibly) resolve the clip and blitter
196 SkAAClipBlitterWrapper wrapper(clip, blitter);
197 blitter = wrapper.getBlitter();
skia.committer@gmail.com453995e2012-11-10 02:01:26 +0000198
reed@google.comd729b3e2012-11-09 14:30:48 +0000199 SkRegion::Cliperator clipper(wrapper.getRgn(), outerR);
skia.committer@gmail.com453995e2012-11-10 02:01:26 +0000200
reed868074b2014-06-03 10:53:59 -0700201 if (!clipper.done()) {
reed@google.comd729b3e2012-11-09 14:30:48 +0000202 const SkIRect& cr = clipper.rect();
203 do {
reed@google.comdab9b4f2012-11-19 16:45:14 +0000204 draw_nine_clipped(mask, outerR, center, fillCenter, cr, blitter);
reed@google.comd729b3e2012-11-09 14:30:48 +0000205 clipper.next();
206 } while (!clipper.done());
207 }
208}
209
reed@google.comdab9b4f2012-11-19 16:45:14 +0000210static int countNestedRects(const SkPath& path, SkRect rects[2]) {
caryclark95bc5f32015-04-08 08:34:15 -0700211 if (path.isNestedFillRects(rects)) {
reed@google.comdab9b4f2012-11-19 16:45:14 +0000212 return 2;
213 }
214 return path.isRect(&rects[0]);
215}
216
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000217bool SkMaskFilter::filterRRect(const SkRRect& devRRect, const SkMatrix& matrix,
reed868074b2014-06-03 10:53:59 -0700218 const SkRasterClip& clip, SkBlitter* blitter,
219 SkPaint::Style style) const {
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000220 // Attempt to speed up drawing by creating a nine patch. If a nine patch
221 // cannot be used, return false to allow our caller to recover and perform
222 // the drawing another way.
223 NinePatch patch;
224 patch.fMask.fImage = NULL;
225 if (kTrue_FilterReturn != this->filterRRectToNine(devRRect, matrix,
226 clip.getBounds(),
227 &patch)) {
228 SkASSERT(NULL == patch.fMask.fImage);
229 return false;
230 }
reed868074b2014-06-03 10:53:59 -0700231 draw_nine(patch.fMask, patch.fOuterRect, patch.fCenter, true, clip, blitter);
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000232 return true;
233}
234
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235bool SkMaskFilter::filterPath(const SkPath& devPath, const SkMatrix& matrix,
reed868074b2014-06-03 10:53:59 -0700236 const SkRasterClip& clip, SkBlitter* blitter,
237 SkPaint::Style style) const {
reed@google.comdab9b4f2012-11-19 16:45:14 +0000238 SkRect rects[2];
239 int rectCount = 0;
reed@google.com5f0add32012-11-26 16:47:10 +0000240 if (SkPaint::kFill_Style == style) {
reed@google.comdab9b4f2012-11-19 16:45:14 +0000241 rectCount = countNestedRects(devPath, rects);
242 }
243 if (rectCount > 0) {
244 NinePatch patch;
reed@google.comd729b3e2012-11-09 14:30:48 +0000245
reedb0df8be2015-02-04 09:07:17 -0800246 switch (this->filterRectsToNine(rects, rectCount, matrix, clip.getBounds(), &patch)) {
reed@google.comd729b3e2012-11-09 14:30:48 +0000247 case kFalse_FilterReturn:
reed@google.comdab9b4f2012-11-19 16:45:14 +0000248 SkASSERT(NULL == patch.fMask.fImage);
reed@google.comd729b3e2012-11-09 14:30:48 +0000249 return false;
reed@google.comdab9b4f2012-11-19 16:45:14 +0000250
reed@google.comd729b3e2012-11-09 14:30:48 +0000251 case kTrue_FilterReturn:
reed868074b2014-06-03 10:53:59 -0700252 draw_nine(patch.fMask, patch.fOuterRect, patch.fCenter, 1 == rectCount, clip,
253 blitter);
reed@google.comd729b3e2012-11-09 14:30:48 +0000254 return true;
reed@google.comdab9b4f2012-11-19 16:45:14 +0000255
reed@google.comd729b3e2012-11-09 14:30:48 +0000256 case kUnimplemented_FilterReturn:
reed@google.comdab9b4f2012-11-19 16:45:14 +0000257 SkASSERT(NULL == patch.fMask.fImage);
reed@google.comd729b3e2012-11-09 14:30:48 +0000258 // fall through
259 break;
260 }
261 }
262
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 SkMask srcM, dstM;
264
265 if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), this, &matrix, &srcM,
junov@chromium.org2ac4ef52012-04-04 15:16:51 +0000266 SkMask::kComputeBoundsAndRenderImage_CreateMode,
267 style)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 return false;
269 }
bungeman@google.com02f55842011-10-04 21:25:00 +0000270 SkAutoMaskFreeImage autoSrc(srcM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800272 if (!this->filterMask(&dstM, srcM, matrix, NULL)) {
273 return false;
reed@google.comfeb8cc82011-04-19 20:11:25 +0000274 }
bungeman@google.com02f55842011-10-04 21:25:00 +0000275 SkAutoMaskFreeImage autoDst(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276
reed@google.com045e62d2011-10-24 12:19:46 +0000277 // if we get here, we need to (possibly) resolve the clip and blitter
278 SkAAClipBlitterWrapper wrapper(clip, blitter);
279 blitter = wrapper.getBlitter();
280
281 SkRegion::Cliperator clipper(wrapper.getRgn(), dstM.fBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282
reed868074b2014-06-03 10:53:59 -0700283 if (!clipper.done()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 const SkIRect& cr = clipper.rect();
285 do {
286 blitter->blitMask(dstM, cr);
287 clipper.next();
288 } while (!clipper.done());
289 }
290
291 return true;
292}
293
reed@google.comd729b3e2012-11-09 14:30:48 +0000294SkMaskFilter::FilterReturn
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000295SkMaskFilter::filterRRectToNine(const SkRRect&, const SkMatrix&,
296 const SkIRect& clipBounds, NinePatch*) const {
297 return kUnimplemented_FilterReturn;
298}
299
300SkMaskFilter::FilterReturn
reed@google.comdab9b4f2012-11-19 16:45:14 +0000301SkMaskFilter::filterRectsToNine(const SkRect[], int count, const SkMatrix&,
reed@google.com30711b72012-12-18 19:18:39 +0000302 const SkIRect& clipBounds, NinePatch*) const {
reed@google.comd729b3e2012-11-09 14:30:48 +0000303 return kUnimplemented_FilterReturn;
304}
305
robertphillips@google.com0b828522013-07-03 15:49:05 +0000306#if SK_SUPPORT_GPU
joshualittb0a8a372014-09-23 09:50:21 -0700307bool SkMaskFilter::asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&) const {
robertphillips@google.com49149312013-07-03 15:34:35 +0000308 return false;
309}
310
skia.committer@gmail.com1842adf2013-07-04 07:01:07 +0000311bool SkMaskFilter::canFilterMaskGPU(const SkRect& devBounds,
robertphillips@google.com49149312013-07-03 15:34:35 +0000312 const SkIRect& clipBounds,
313 const SkMatrix& ctm,
314 SkRect* maskRect) const {
315 return false;
316}
317
robertphillipsff0ca5e2015-07-22 11:54:44 -0700318 bool SkMaskFilter::directFilterMaskGPU(GrTextureProvider* texProvider,
319 GrDrawContext* drawContext,
joshualitt25d9c152015-02-18 12:29:52 -0800320 GrRenderTarget* rt,
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000321 GrPaint* grp,
joshualitt570d2f82015-02-25 13:19:48 -0800322 const GrClip&,
joshualitt5531d512014-12-17 15:50:11 -0800323 const SkMatrix& viewMatrix,
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000324 const SkStrokeRec& strokeRec,
325 const SkPath& path) const {
326 return false;
327}
328
robertphillips@google.com49149312013-07-03 15:34:35 +0000329
robertphillipsff0ca5e2015-07-22 11:54:44 -0700330bool SkMaskFilter::directFilterRRectMaskGPU(GrTextureProvider* texProvider,
331 GrDrawContext* drawContext,
joshualitt25d9c152015-02-18 12:29:52 -0800332 GrRenderTarget* rt,
commit-bot@chromium.org82139702014-03-10 22:53:20 +0000333 GrPaint* grp,
joshualitt570d2f82015-02-25 13:19:48 -0800334 const GrClip&,
joshualitt5531d512014-12-17 15:50:11 -0800335 const SkMatrix& viewMatrix,
commit-bot@chromium.org82139702014-03-10 22:53:20 +0000336 const SkStrokeRec& strokeRec,
337 const SkRRect& rrect) const {
338 return false;
339}
340
skia.committer@gmail.com1842adf2013-07-04 07:01:07 +0000341bool SkMaskFilter::filterMaskGPU(GrTexture* src,
commit-bot@chromium.org41bf9302014-01-08 22:25:53 +0000342 const SkMatrix& ctm,
skia.committer@gmail.com1842adf2013-07-04 07:01:07 +0000343 const SkRect& maskRect,
robertphillips@google.com49149312013-07-03 15:34:35 +0000344 GrTexture** result,
345 bool canOverwriteSrc) const {
346 return false;
reed@google.com2b75f422011-07-07 13:43:38 +0000347}
robertphillips@google.com0b828522013-07-03 15:49:05 +0000348#endif
reed@google.com2b75f422011-07-07 13:43:38 +0000349
reed@google.com30711b72012-12-18 19:18:39 +0000350void SkMaskFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
reed@google.com9efd9a02012-01-30 15:41:43 +0000351 SkMask srcM, dstM;
352
353 srcM.fImage = NULL;
reedb07a94f2014-11-19 05:03:18 -0800354 srcM.fBounds = src.roundOut();
reed@google.com9efd9a02012-01-30 15:41:43 +0000355 srcM.fRowBytes = 0;
356 srcM.fFormat = SkMask::kA8_Format;
357
358 SkIPoint margin; // ignored
359 if (this->filterMask(&dstM, srcM, SkMatrix::I(), &margin)) {
360 dst->set(dstM.fBounds);
361 } else {
362 dst->set(srcM.fBounds);
363 }
364}