blob: 5e7cee6bd6d0eb85aa82fdeddf4f08087c3fbfd9 [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"
bungemand3ebb482015-08-05 13:57:49 -070014#include "SkPath.h"
reed@google.com045e62d2011-10-24 12:19:46 +000015#include "SkRasterClip.h"
scroggo@google.coma8e33a92013-11-08 18:02:53 +000016#include "SkRRect.h"
robertphillips@google.com49149312013-07-03 15:34:35 +000017#include "SkTypes.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000018
robertphillips@google.com0b828522013-07-03 15:49:05 +000019#if SK_SUPPORT_GPU
20#include "GrTexture.h"
21#include "SkGr.h"
22#include "SkGrPixelRef.h"
23#endif
reed@google.comd729b3e2012-11-09 14:30:48 +000024
reedb0df8be2015-02-04 09:07:17 -080025SkMaskFilter::NinePatch::~NinePatch() {
26 if (fCache) {
27 SkASSERT((const void*)fMask.fImage == fCache->data());
28 fCache->unref();
29 } else {
30 SkMask::FreeImage(fMask.fImage);
31 }
32}
33
reed@google.comfeb8cc82011-04-19 20:11:25 +000034bool SkMaskFilter::filterMask(SkMask*, const SkMask&, const SkMatrix&,
reed@google.com30711b72012-12-18 19:18:39 +000035 SkIPoint*) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +000036 return false;
37}
38
reed@google.comdaaafa62014-04-29 15:20:16 +000039bool SkMaskFilter::asABlur(BlurRec*) const {
40 return false;
41}
42
reed@google.comd729b3e2012-11-09 14:30:48 +000043static void extractMaskSubset(const SkMask& src, SkMask* dst) {
44 SkASSERT(src.fBounds.contains(dst->fBounds));
45
46 const int dx = dst->fBounds.left() - src.fBounds.left();
47 const int dy = dst->fBounds.top() - src.fBounds.top();
48 dst->fImage = src.fImage + dy * src.fRowBytes + dx;
49 dst->fRowBytes = src.fRowBytes;
50 dst->fFormat = src.fFormat;
51}
52
53static void blitClippedMask(SkBlitter* blitter, const SkMask& mask,
54 const SkIRect& bounds, const SkIRect& clipR) {
55 SkIRect r;
56 if (r.intersect(bounds, clipR)) {
57 blitter->blitMask(mask, r);
58 }
59}
60
61static void blitClippedRect(SkBlitter* blitter, const SkIRect& rect, const SkIRect& clipR) {
62 SkIRect r;
63 if (r.intersect(rect, clipR)) {
64 blitter->blitRect(r.left(), r.top(), r.width(), r.height());
65 }
66}
67
68#if 0
69static void dump(const SkMask& mask) {
70 for (int y = mask.fBounds.top(); y < mask.fBounds.bottom(); ++y) {
71 for (int x = mask.fBounds.left(); x < mask.fBounds.right(); ++x) {
72 SkDebugf("%02X", *mask.getAddr8(x, y));
73 }
74 SkDebugf("\n");
75 }
76 SkDebugf("\n");
77}
78#endif
79
80static void draw_nine_clipped(const SkMask& mask, const SkIRect& outerR,
reed@google.comdab9b4f2012-11-19 16:45:14 +000081 const SkIPoint& center, bool fillCenter,
reed@google.comd729b3e2012-11-09 14:30:48 +000082 const SkIRect& clipR, SkBlitter* blitter) {
reed@google.comdab9b4f2012-11-19 16:45:14 +000083 int cx = center.x();
84 int cy = center.y();
reed@google.comd729b3e2012-11-09 14:30:48 +000085 SkMask m;
skia.committer@gmail.com453995e2012-11-10 02:01:26 +000086
reed@google.comd729b3e2012-11-09 14:30:48 +000087 // top-left
88 m.fBounds = mask.fBounds;
89 m.fBounds.fRight = cx;
90 m.fBounds.fBottom = cy;
tomhudson@google.com3c0ecc52013-04-25 16:56:40 +000091 if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
92 extractMaskSubset(mask, &m);
93 m.fBounds.offsetTo(outerR.left(), outerR.top());
94 blitClippedMask(blitter, m, m.fBounds, clipR);
95 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +000096
reed@google.comd729b3e2012-11-09 14:30:48 +000097 // top-right
98 m.fBounds = mask.fBounds;
99 m.fBounds.fLeft = cx + 1;
100 m.fBounds.fBottom = cy;
tomhudson@google.com3c0ecc52013-04-25 16:56:40 +0000101 if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
102 extractMaskSubset(mask, &m);
103 m.fBounds.offsetTo(outerR.right() - m.fBounds.width(), outerR.top());
104 blitClippedMask(blitter, m, m.fBounds, clipR);
105 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +0000106
reed@google.comd729b3e2012-11-09 14:30:48 +0000107 // bottom-left
108 m.fBounds = mask.fBounds;
109 m.fBounds.fRight = cx;
110 m.fBounds.fTop = cy + 1;
tomhudson@google.com3c0ecc52013-04-25 16:56:40 +0000111 if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
112 extractMaskSubset(mask, &m);
113 m.fBounds.offsetTo(outerR.left(), outerR.bottom() - m.fBounds.height());
114 blitClippedMask(blitter, m, m.fBounds, clipR);
115 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +0000116
reed@google.comd729b3e2012-11-09 14:30:48 +0000117 // bottom-right
118 m.fBounds = mask.fBounds;
119 m.fBounds.fLeft = cx + 1;
120 m.fBounds.fTop = cy + 1;
tomhudson@google.com3c0ecc52013-04-25 16:56:40 +0000121 if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
122 extractMaskSubset(mask, &m);
123 m.fBounds.offsetTo(outerR.right() - m.fBounds.width(),
124 outerR.bottom() - m.fBounds.height());
125 blitClippedMask(blitter, m, m.fBounds, clipR);
126 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000127
128 SkIRect innerR;
129 innerR.set(outerR.left() + cx - mask.fBounds.left(),
130 outerR.top() + cy - mask.fBounds.top(),
131 outerR.right() + (cx + 1 - mask.fBounds.right()),
132 outerR.bottom() + (cy + 1 - mask.fBounds.bottom()));
reed@google.comdab9b4f2012-11-19 16:45:14 +0000133 if (fillCenter) {
134 blitClippedRect(blitter, innerR, clipR);
135 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000136
137 const int innerW = innerR.width();
138 size_t storageSize = (innerW + 1) * (sizeof(int16_t) + sizeof(uint8_t));
139 SkAutoSMalloc<4*1024> storage(storageSize);
140 int16_t* runs = (int16_t*)storage.get();
141 uint8_t* alpha = (uint8_t*)(runs + innerW + 1);
142
143 SkIRect r;
144 // top
145 r.set(innerR.left(), outerR.top(), innerR.right(), innerR.top());
146 if (r.intersect(clipR)) {
147 int startY = SkMax32(0, r.top() - outerR.top());
148 int stopY = startY + r.height();
149 int width = r.width();
150 for (int y = startY; y < stopY; ++y) {
151 runs[0] = width;
152 runs[width] = 0;
153 alpha[0] = *mask.getAddr8(cx, mask.fBounds.top() + y);
154 blitter->blitAntiH(r.left(), outerR.top() + y, alpha, runs);
155 }
156 }
157 // bottom
158 r.set(innerR.left(), innerR.bottom(), innerR.right(), outerR.bottom());
159 if (r.intersect(clipR)) {
160 int startY = outerR.bottom() - r.bottom();
161 int stopY = startY + r.height();
162 int width = r.width();
163 for (int y = startY; y < stopY; ++y) {
164 runs[0] = width;
165 runs[width] = 0;
166 alpha[0] = *mask.getAddr8(cx, mask.fBounds.bottom() - y - 1);
167 blitter->blitAntiH(r.left(), outerR.bottom() - y - 1, alpha, runs);
168 }
169 }
170 // left
171 r.set(outerR.left(), innerR.top(), innerR.left(), innerR.bottom());
172 if (r.intersect(clipR)) {
173 int startX = r.left() - outerR.left();
174 int stopX = startX + r.width();
175 int height = r.height();
176 for (int x = startX; x < stopX; ++x) {
177 blitter->blitV(outerR.left() + x, r.top(), height,
178 *mask.getAddr8(mask.fBounds.left() + x, mask.fBounds.top() + cy));
179 }
180 }
181 // right
182 r.set(innerR.right(), innerR.top(), outerR.right(), innerR.bottom());
183 if (r.intersect(clipR)) {
184 int startX = outerR.right() - r.right();
185 int stopX = startX + r.width();
186 int height = r.height();
187 for (int x = startX; x < stopX; ++x) {
188 blitter->blitV(outerR.right() - x - 1, r.top(), height,
189 *mask.getAddr8(mask.fBounds.right() - x - 1, mask.fBounds.top() + cy));
190 }
191 }
192}
193
reed868074b2014-06-03 10:53:59 -0700194static void draw_nine(const SkMask& mask, const SkIRect& outerR, const SkIPoint& center,
195 bool fillCenter, const SkRasterClip& clip, SkBlitter* blitter) {
reed@google.comd729b3e2012-11-09 14:30:48 +0000196 // if we get here, we need to (possibly) resolve the clip and blitter
197 SkAAClipBlitterWrapper wrapper(clip, blitter);
198 blitter = wrapper.getBlitter();
skia.committer@gmail.com453995e2012-11-10 02:01:26 +0000199
reed@google.comd729b3e2012-11-09 14:30:48 +0000200 SkRegion::Cliperator clipper(wrapper.getRgn(), outerR);
skia.committer@gmail.com453995e2012-11-10 02:01:26 +0000201
reed868074b2014-06-03 10:53:59 -0700202 if (!clipper.done()) {
reed@google.comd729b3e2012-11-09 14:30:48 +0000203 const SkIRect& cr = clipper.rect();
204 do {
reed@google.comdab9b4f2012-11-19 16:45:14 +0000205 draw_nine_clipped(mask, outerR, center, fillCenter, cr, blitter);
reed@google.comd729b3e2012-11-09 14:30:48 +0000206 clipper.next();
207 } while (!clipper.done());
208 }
209}
210
reed@google.comdab9b4f2012-11-19 16:45:14 +0000211static int countNestedRects(const SkPath& path, SkRect rects[2]) {
caryclark95bc5f32015-04-08 08:34:15 -0700212 if (path.isNestedFillRects(rects)) {
reed@google.comdab9b4f2012-11-19 16:45:14 +0000213 return 2;
214 }
215 return path.isRect(&rects[0]);
216}
217
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000218bool SkMaskFilter::filterRRect(const SkRRect& devRRect, const SkMatrix& matrix,
reed868074b2014-06-03 10:53:59 -0700219 const SkRasterClip& clip, SkBlitter* blitter,
220 SkPaint::Style style) const {
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000221 // Attempt to speed up drawing by creating a nine patch. If a nine patch
222 // cannot be used, return false to allow our caller to recover and perform
223 // the drawing another way.
224 NinePatch patch;
225 patch.fMask.fImage = NULL;
226 if (kTrue_FilterReturn != this->filterRRectToNine(devRRect, matrix,
227 clip.getBounds(),
228 &patch)) {
229 SkASSERT(NULL == patch.fMask.fImage);
230 return false;
231 }
reed868074b2014-06-03 10:53:59 -0700232 draw_nine(patch.fMask, patch.fOuterRect, patch.fCenter, true, clip, blitter);
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000233 return true;
234}
235
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236bool SkMaskFilter::filterPath(const SkPath& devPath, const SkMatrix& matrix,
reed868074b2014-06-03 10:53:59 -0700237 const SkRasterClip& clip, SkBlitter* blitter,
238 SkPaint::Style style) const {
reed@google.comdab9b4f2012-11-19 16:45:14 +0000239 SkRect rects[2];
240 int rectCount = 0;
reed@google.com5f0add32012-11-26 16:47:10 +0000241 if (SkPaint::kFill_Style == style) {
reed@google.comdab9b4f2012-11-19 16:45:14 +0000242 rectCount = countNestedRects(devPath, rects);
243 }
244 if (rectCount > 0) {
245 NinePatch patch;
reed@google.comd729b3e2012-11-09 14:30:48 +0000246
reedb0df8be2015-02-04 09:07:17 -0800247 switch (this->filterRectsToNine(rects, rectCount, matrix, clip.getBounds(), &patch)) {
reed@google.comd729b3e2012-11-09 14:30:48 +0000248 case kFalse_FilterReturn:
reed@google.comdab9b4f2012-11-19 16:45:14 +0000249 SkASSERT(NULL == patch.fMask.fImage);
reed@google.comd729b3e2012-11-09 14:30:48 +0000250 return false;
reed@google.comdab9b4f2012-11-19 16:45:14 +0000251
reed@google.comd729b3e2012-11-09 14:30:48 +0000252 case kTrue_FilterReturn:
reed868074b2014-06-03 10:53:59 -0700253 draw_nine(patch.fMask, patch.fOuterRect, patch.fCenter, 1 == rectCount, clip,
254 blitter);
reed@google.comd729b3e2012-11-09 14:30:48 +0000255 return true;
reed@google.comdab9b4f2012-11-19 16:45:14 +0000256
reed@google.comd729b3e2012-11-09 14:30:48 +0000257 case kUnimplemented_FilterReturn:
reed@google.comdab9b4f2012-11-19 16:45:14 +0000258 SkASSERT(NULL == patch.fMask.fImage);
reed@google.comd729b3e2012-11-09 14:30:48 +0000259 // fall through
260 break;
261 }
262 }
263
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 SkMask srcM, dstM;
265
266 if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), this, &matrix, &srcM,
junov@chromium.org2ac4ef52012-04-04 15:16:51 +0000267 SkMask::kComputeBoundsAndRenderImage_CreateMode,
268 style)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 return false;
270 }
bungeman@google.com02f55842011-10-04 21:25:00 +0000271 SkAutoMaskFreeImage autoSrc(srcM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800273 if (!this->filterMask(&dstM, srcM, matrix, NULL)) {
274 return false;
reed@google.comfeb8cc82011-04-19 20:11:25 +0000275 }
bungeman@google.com02f55842011-10-04 21:25:00 +0000276 SkAutoMaskFreeImage autoDst(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277
reed@google.com045e62d2011-10-24 12:19:46 +0000278 // if we get here, we need to (possibly) resolve the clip and blitter
279 SkAAClipBlitterWrapper wrapper(clip, blitter);
280 blitter = wrapper.getBlitter();
281
282 SkRegion::Cliperator clipper(wrapper.getRgn(), dstM.fBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283
reed868074b2014-06-03 10:53:59 -0700284 if (!clipper.done()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 const SkIRect& cr = clipper.rect();
286 do {
287 blitter->blitMask(dstM, cr);
288 clipper.next();
289 } while (!clipper.done());
290 }
291
292 return true;
293}
294
reed@google.comd729b3e2012-11-09 14:30:48 +0000295SkMaskFilter::FilterReturn
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000296SkMaskFilter::filterRRectToNine(const SkRRect&, const SkMatrix&,
297 const SkIRect& clipBounds, NinePatch*) const {
298 return kUnimplemented_FilterReturn;
299}
300
301SkMaskFilter::FilterReturn
reed@google.comdab9b4f2012-11-19 16:45:14 +0000302SkMaskFilter::filterRectsToNine(const SkRect[], int count, const SkMatrix&,
reed@google.com30711b72012-12-18 19:18:39 +0000303 const SkIRect& clipBounds, NinePatch*) const {
reed@google.comd729b3e2012-11-09 14:30:48 +0000304 return kUnimplemented_FilterReturn;
305}
306
robertphillips@google.com0b828522013-07-03 15:49:05 +0000307#if SK_SUPPORT_GPU
joshualittb0a8a372014-09-23 09:50:21 -0700308bool SkMaskFilter::asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&) const {
robertphillips@google.com49149312013-07-03 15:34:35 +0000309 return false;
310}
311
skia.committer@gmail.com1842adf2013-07-04 07:01:07 +0000312bool SkMaskFilter::canFilterMaskGPU(const SkRect& devBounds,
robertphillips@google.com49149312013-07-03 15:34:35 +0000313 const SkIRect& clipBounds,
314 const SkMatrix& ctm,
315 SkRect* maskRect) const {
316 return false;
317}
318
robertphillipsff0ca5e2015-07-22 11:54:44 -0700319 bool SkMaskFilter::directFilterMaskGPU(GrTextureProvider* texProvider,
320 GrDrawContext* drawContext,
joshualitt25d9c152015-02-18 12:29:52 -0800321 GrRenderTarget* rt,
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000322 GrPaint* grp,
joshualitt570d2f82015-02-25 13:19:48 -0800323 const GrClip&,
joshualitt5531d512014-12-17 15:50:11 -0800324 const SkMatrix& viewMatrix,
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000325 const SkStrokeRec& strokeRec,
326 const SkPath& path) const {
327 return false;
328}
329
robertphillips@google.com49149312013-07-03 15:34:35 +0000330
robertphillipsff0ca5e2015-07-22 11:54:44 -0700331bool SkMaskFilter::directFilterRRectMaskGPU(GrTextureProvider* texProvider,
332 GrDrawContext* drawContext,
joshualitt25d9c152015-02-18 12:29:52 -0800333 GrRenderTarget* rt,
commit-bot@chromium.org82139702014-03-10 22:53:20 +0000334 GrPaint* grp,
joshualitt570d2f82015-02-25 13:19:48 -0800335 const GrClip&,
joshualitt5531d512014-12-17 15:50:11 -0800336 const SkMatrix& viewMatrix,
commit-bot@chromium.org82139702014-03-10 22:53:20 +0000337 const SkStrokeRec& strokeRec,
338 const SkRRect& rrect) const {
339 return false;
340}
341
skia.committer@gmail.com1842adf2013-07-04 07:01:07 +0000342bool SkMaskFilter::filterMaskGPU(GrTexture* src,
commit-bot@chromium.org41bf9302014-01-08 22:25:53 +0000343 const SkMatrix& ctm,
skia.committer@gmail.com1842adf2013-07-04 07:01:07 +0000344 const SkRect& maskRect,
robertphillips@google.com49149312013-07-03 15:34:35 +0000345 GrTexture** result,
346 bool canOverwriteSrc) const {
347 return false;
reed@google.com2b75f422011-07-07 13:43:38 +0000348}
robertphillips@google.com0b828522013-07-03 15:49:05 +0000349#endif
reed@google.com2b75f422011-07-07 13:43:38 +0000350
reed@google.com30711b72012-12-18 19:18:39 +0000351void SkMaskFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
reed@google.com9efd9a02012-01-30 15:41:43 +0000352 SkMask srcM, dstM;
353
354 srcM.fImage = NULL;
reedb07a94f2014-11-19 05:03:18 -0800355 srcM.fBounds = src.roundOut();
reed@google.com9efd9a02012-01-30 15:41:43 +0000356 srcM.fRowBytes = 0;
357 srcM.fFormat = SkMask::kA8_Format;
358
359 SkIPoint margin; // ignored
360 if (this->filterMask(&dstM, srcM, SkMatrix::I(), &margin)) {
361 dst->set(dstM.fBounds);
362 } else {
363 dst->set(srcM.fBounds);
364 }
365}