blob: 2329b0f685ab5a8d61a471e281a046e5431aa988 [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#include "SkBlurMaskFilter.h"
10#include "SkBlurMask.h"
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000011#include "SkFlattenableBuffers.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkMaskFilter.h"
humper@google.com7c5d7b72013-03-11 20:16:28 +000013#include "SkBounder.h"
14#include "SkRasterClip.h"
15#include "SkRTConf.h"
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +000016#include "SkStringUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000017
18class SkBlurMaskFilterImpl : public SkMaskFilter {
19public:
reed@google.com03016a32011-08-12 14:59:59 +000020 SkBlurMaskFilterImpl(SkScalar radius, SkBlurMaskFilter::BlurStyle,
21 uint32_t flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +000022
23 // overrides from SkMaskFilter
reed@google.com30711b72012-12-18 19:18:39 +000024 virtual SkMask::Format getFormat() const SK_OVERRIDE;
reed@google.com03016a32011-08-12 14:59:59 +000025 virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
reed@google.com30711b72012-12-18 19:18:39 +000026 SkIPoint* margin) const SK_OVERRIDE;
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +000027
bungeman@google.com18ac1ba2011-10-04 15:11:38 +000028 virtual BlurType asABlur(BlurInfo*) const SK_OVERRIDE;
reed@google.com30711b72012-12-18 19:18:39 +000029 virtual void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +000030
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +000031 SkDEVCODE(virtual void toString(SkString* str) const SK_OVERRIDE;)
djsollen@google.comba28d032012-03-26 17:57:35 +000032 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl)
reed@android.com8a1c16f2008-12-17 15:59:43 +000033
reed@google.comd729b3e2012-11-09 14:30:48 +000034protected:
reed@google.comdab9b4f2012-11-19 16:45:14 +000035 virtual FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&,
36 const SkIRect& clipBounds,
reed@google.com30711b72012-12-18 19:18:39 +000037 NinePatch*) const SK_OVERRIDE;
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +000038
39 bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix,
humper@google.com7c5d7b72013-03-11 20:16:28 +000040 SkIPoint* margin, SkMask::CreateMode createMode) const;
skia.committer@gmail.com453995e2012-11-10 02:01:26 +000041
reed@android.com8a1c16f2008-12-17 15:59:43 +000042private:
43 SkScalar fRadius;
44 SkBlurMaskFilter::BlurStyle fBlurStyle;
senorblanco@chromium.org038aff62010-12-06 23:45:58 +000045 uint32_t fBlurFlags;
reed@android.com8a1c16f2008-12-17 15:59:43 +000046
47 SkBlurMaskFilterImpl(SkFlattenableReadBuffer&);
djsollen@google.com54924242012-03-29 15:18:04 +000048 virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
rmistry@google.comfbfcd562012-08-23 18:09:54 +000049
reed@android.com8a1c16f2008-12-17 15:59:43 +000050 typedef SkMaskFilter INHERITED;
51};
52
reed@google.com03016a32011-08-12 14:59:59 +000053SkMaskFilter* SkBlurMaskFilter::Create(SkScalar radius,
54 SkBlurMaskFilter::BlurStyle style,
55 uint32_t flags) {
reed@google.come5828692012-01-11 14:07:27 +000056 // use !(radius > 0) instead of radius <= 0 to reject NaN values
rmistry@google.comfbfcd562012-08-23 18:09:54 +000057 if (!(radius > 0) || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount
reed@google.com03016a32011-08-12 14:59:59 +000058 || flags > SkBlurMaskFilter::kAll_BlurFlag) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000059 return NULL;
reed@google.com03016a32011-08-12 14:59:59 +000060 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000061
senorblanco@chromium.org038aff62010-12-06 23:45:58 +000062 return SkNEW_ARGS(SkBlurMaskFilterImpl, (radius, style, flags));
reed@android.com8a1c16f2008-12-17 15:59:43 +000063}
64
reed@google.com03016a32011-08-12 14:59:59 +000065///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000066
reed@google.com03016a32011-08-12 14:59:59 +000067SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar radius,
68 SkBlurMaskFilter::BlurStyle style,
senorblanco@chromium.org038aff62010-12-06 23:45:58 +000069 uint32_t flags)
reed@google.com03016a32011-08-12 14:59:59 +000070 : fRadius(radius), fBlurStyle(style), fBlurFlags(flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000071#if 0
72 fGamma = NULL;
reed@google.com03016a32011-08-12 14:59:59 +000073 if (gammaScale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000074 fGamma = new U8[256];
75 if (gammaScale > 0)
76 SkBlurMask::BuildSqrGamma(fGamma, gammaScale);
77 else
78 SkBlurMask::BuildSqrtGamma(fGamma, -gammaScale);
79 }
80#endif
81 SkASSERT(radius >= 0);
82 SkASSERT((unsigned)style < SkBlurMaskFilter::kBlurStyleCount);
senorblanco@chromium.org038aff62010-12-06 23:45:58 +000083 SkASSERT(flags <= SkBlurMaskFilter::kAll_BlurFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +000084}
85
reed@google.com30711b72012-12-18 19:18:39 +000086SkMask::Format SkBlurMaskFilterImpl::getFormat() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 return SkMask::kA8_Format;
88}
89
reed@google.com03016a32011-08-12 14:59:59 +000090bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
reed@google.com30711b72012-12-18 19:18:39 +000091 const SkMatrix& matrix,
92 SkIPoint* margin) const{
senorblanco@chromium.org038aff62010-12-06 23:45:58 +000093 SkScalar radius;
reed@google.com03016a32011-08-12 14:59:59 +000094 if (fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) {
senorblanco@chromium.org038aff62010-12-06 23:45:58 +000095 radius = fRadius;
reed@google.com03016a32011-08-12 14:59:59 +000096 } else {
senorblanco@chromium.org038aff62010-12-06 23:45:58 +000097 radius = matrix.mapRadius(fRadius);
reed@google.com03016a32011-08-12 14:59:59 +000098 }
senorblanco@chromium.org038aff62010-12-06 23:45:58 +000099
reed@android.com35555912009-03-16 18:46:55 +0000100 // To avoid unseemly allocation requests (esp. for finite platforms like
101 // handset) we limit the radius so something manageable. (as opposed to
102 // a request like 10,000)
103 static const SkScalar MAX_RADIUS = SkIntToScalar(128);
104 radius = SkMinScalar(radius, MAX_RADIUS);
reed@google.com03016a32011-08-12 14:59:59 +0000105 SkBlurMask::Quality blurQuality =
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000106 (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ?
reed@google.com03016a32011-08-12 14:59:59 +0000107 SkBlurMask::kHigh_Quality : SkBlurMask::kLow_Quality;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108
senorblanco@chromium.org0cb6e532012-11-29 19:00:22 +0000109#ifndef SK_DISABLE_SEPARABLE_MASK_BLUR
senorblanco@chromium.org91f489a2012-11-29 17:09:27 +0000110 return SkBlurMask::BlurSeparable(dst, src, radius, (SkBlurMask::Style)fBlurStyle,
111 blurQuality, margin);
112#else
113 return SkBlurMask::Blur(dst, src, radius, (SkBlurMask::Style)fBlurStyle,
114 blurQuality, margin);
115#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116}
117
humper@google.com7c5d7b72013-03-11 20:16:28 +0000118bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r,
119 const SkMatrix& matrix,
120 SkIPoint* margin, SkMask::CreateMode createMode) const{
121 SkScalar radius;
122 if (fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) {
123 radius = fRadius;
124 } else {
125 radius = matrix.mapRadius(fRadius);
126 }
127
128 // To avoid unseemly allocation requests (esp. for finite platforms like
129 // handset) we limit the radius so something manageable. (as opposed to
130 // a request like 10,000)
131 static const SkScalar MAX_RADIUS = SkIntToScalar(128);
132 radius = SkMinScalar(radius, MAX_RADIUS);
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +0000133
humper@google.com7c5d7b72013-03-11 20:16:28 +0000134 return SkBlurMask::BlurRect(dst, r, radius, (SkBlurMask::Style)fBlurStyle,
135 margin, createMode);
136}
137
reed@google.comd729b3e2012-11-09 14:30:48 +0000138#include "SkCanvas.h"
139
reed@google.comdab9b4f2012-11-19 16:45:14 +0000140static bool drawRectsIntoMask(const SkRect rects[], int count, SkMask* mask) {
141 rects[0].roundOut(&mask->fBounds);
reed@google.comd729b3e2012-11-09 14:30:48 +0000142 mask->fRowBytes = SkAlign4(mask->fBounds.width());
143 mask->fFormat = SkMask::kA8_Format;
144 size_t size = mask->computeImageSize();
145 mask->fImage = SkMask::AllocImage(size);
146 if (NULL == mask->fImage) {
147 return false;
148 }
149 sk_bzero(mask->fImage, size);
150
151 SkBitmap bitmap;
152 bitmap.setConfig(SkBitmap::kA8_Config,
153 mask->fBounds.width(), mask->fBounds.height(),
154 mask->fRowBytes);
155 bitmap.setPixels(mask->fImage);
156
157 SkCanvas canvas(bitmap);
reed@google.comdab9b4f2012-11-19 16:45:14 +0000158 canvas.translate(-SkIntToScalar(mask->fBounds.left()),
159 -SkIntToScalar(mask->fBounds.top()));
reed@google.comd729b3e2012-11-09 14:30:48 +0000160
161 SkPaint paint;
162 paint.setAntiAlias(true);
163
reed@google.comdab9b4f2012-11-19 16:45:14 +0000164 if (1 == count) {
165 canvas.drawRect(rects[0], paint);
166 } else {
167 // todo: do I need a fast way to do this?
168 SkPath path;
169 path.addRect(rects[0]);
170 path.addRect(rects[1]);
171 path.setFillType(SkPath::kEvenOdd_FillType);
172 canvas.drawPath(path, paint);
173 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000174 return true;
175}
176
reed@google.comf276fa72012-11-21 14:14:10 +0000177static bool rect_exceeds(const SkRect& r, SkScalar v) {
178 return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v ||
179 r.width() > v || r.height() > v;
reed@google.com07784a02012-11-19 21:09:14 +0000180}
181
humper@google.com7c5d7b72013-03-11 20:16:28 +0000182#ifdef SK_IGNORE_FAST_RECT_BLUR
humper@google.come86af1f2013-03-11 21:23:36 +0000183SK_CONF_DECLARE( bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch", false, "Use the faster analytic blur approach for ninepatch rects" );
humper@google.com7c5d7b72013-03-11 20:16:28 +0000184#else
humper@google.come86af1f2013-03-11 21:23:36 +0000185SK_CONF_DECLARE( bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch", true, "Use the faster analytic blur approach for ninepatch rects" );
humper@google.com7c5d7b72013-03-11 20:16:28 +0000186#endif
humper@google.com7c5d7b72013-03-11 20:16:28 +0000187
reed@google.comd729b3e2012-11-09 14:30:48 +0000188SkMaskFilter::FilterReturn
reed@google.comdab9b4f2012-11-19 16:45:14 +0000189SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count,
190 const SkMatrix& matrix,
191 const SkIRect& clipBounds,
reed@google.com30711b72012-12-18 19:18:39 +0000192 NinePatch* patch) const {
reed@google.comdab9b4f2012-11-19 16:45:14 +0000193 if (count < 1 || count > 2) {
194 return kUnimplemented_FilterReturn;
195 }
skia.committer@gmail.com34587162012-11-20 02:01:23 +0000196
reed@google.com57850b92012-12-17 21:20:53 +0000197 // TODO: report correct metrics for innerstyle, where we do not grow the
198 // total bounds, but we do need an inset the size of our blur-radius
199 if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle) {
200 return kUnimplemented_FilterReturn;
201 }
202
reed@google.com07784a02012-11-19 21:09:14 +0000203 // TODO: take clipBounds into account to limit our coordinates up front
204 // for now, just skip too-large src rects (to take the old code path).
reed@google.comf276fa72012-11-21 14:14:10 +0000205 if (rect_exceeds(rects[0], SkIntToScalar(32767))) {
reed@google.com07784a02012-11-19 21:09:14 +0000206 return kUnimplemented_FilterReturn;
207 }
skia.committer@gmail.com34587162012-11-20 02:01:23 +0000208
reed@google.comd729b3e2012-11-09 14:30:48 +0000209 SkIPoint margin;
210 SkMask srcM, dstM;
reed@google.comdab9b4f2012-11-19 16:45:14 +0000211 rects[0].roundOut(&srcM.fBounds);
reed@google.comd729b3e2012-11-09 14:30:48 +0000212 srcM.fImage = NULL;
213 srcM.fFormat = SkMask::kA8_Format;
214 srcM.fRowBytes = 0;
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +0000215
humper@google.com7c5d7b72013-03-11 20:16:28 +0000216 bool filterResult = false;
217 if (count == 1 && c_analyticBlurNinepatch) {
218 // special case for fast rect blur
219 // don't actually do the blur the first time, just compute the correct size
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +0000220 filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin,
humper@google.com7c5d7b72013-03-11 20:16:28 +0000221 SkMask::kJustComputeBounds_CreateMode);
222 } else {
223 filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
224 }
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +0000225
humper@google.com7c5d7b72013-03-11 20:16:28 +0000226 if (!filterResult) {
reed@google.comd729b3e2012-11-09 14:30:48 +0000227 return kFalse_FilterReturn;
228 }
229
230 /*
231 * smallR is the smallest version of 'rect' that will still guarantee that
232 * we get the same blur results on all edges, plus 1 center row/col that is
233 * representative of the extendible/stretchable edges of the ninepatch.
234 * Since our actual edge may be fractional we inset 1 more to be sure we
235 * don't miss any interior blur.
236 * x is an added pixel of blur, and { and } are the (fractional) edge
237 * pixels from the original rect.
238 *
239 * x x { x x .... x x } x x
240 *
241 * Thus, in this case, we inset by a total of 5 (on each side) beginning
242 * with our outer-rect (dstM.fBounds)
243 */
reed@google.comdab9b4f2012-11-19 16:45:14 +0000244 SkRect smallR[2];
245 SkIPoint center;
246
247 // +2 is from +1 for each edge (to account for possible fractional edges
248 int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2;
249 int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2;
250 SkIRect innerIR;
251
252 if (1 == count) {
253 innerIR = srcM.fBounds;
254 center.set(smallW, smallH);
255 } else {
256 SkASSERT(2 == count);
257 rects[1].roundIn(&innerIR);
258 center.set(smallW + (innerIR.left() - srcM.fBounds.left()),
259 smallH + (innerIR.top() - srcM.fBounds.top()));
reed@google.comd729b3e2012-11-09 14:30:48 +0000260 }
261
reed@google.comdab9b4f2012-11-19 16:45:14 +0000262 // +1 so we get a clean, stretchable, center row/col
263 smallW += 1;
264 smallH += 1;
265
266 // we want the inset amounts to be integral, so we don't change any
267 // fractional phase on the fRight or fBottom of our smallR.
268 const SkScalar dx = SkIntToScalar(innerIR.width() - smallW);
269 const SkScalar dy = SkIntToScalar(innerIR.height() - smallH);
270 if (dx < 0 || dy < 0) {
271 // we're too small, relative to our blur, to break into nine-patch,
272 // so we ask to have our normal filterMask() be called.
273 return kUnimplemented_FilterReturn;
274 }
275
276 smallR[0].set(rects[0].left(), rects[0].top(), rects[0].right() - dx, rects[0].bottom() - dy);
277 SkASSERT(!smallR[0].isEmpty());
278 if (2 == count) {
279 smallR[1].set(rects[1].left(), rects[1].top(),
280 rects[1].right() - dx, rects[1].bottom() - dy);
281 SkASSERT(!smallR[1].isEmpty());
282 }
283
humper@google.com7c5d7b72013-03-11 20:16:28 +0000284 if (count > 1 || !c_analyticBlurNinepatch) {
285 if (!drawRectsIntoMask(smallR, count, &srcM)) {
286 return kFalse_FilterReturn;
287 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000288
humper@google.com7c5d7b72013-03-11 20:16:28 +0000289 SkAutoMaskFreeImage amf(srcM.fImage);
reed@google.comd198a5b2012-11-27 15:18:04 +0000290
humper@google.com7c5d7b72013-03-11 20:16:28 +0000291 if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
292 return kFalse_FilterReturn;
293 }
294 } else {
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +0000295 if (!this->filterRectMask(&patch->fMask, smallR[0], matrix, &margin,
humper@google.com7c5d7b72013-03-11 20:16:28 +0000296 SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
297 return kFalse_FilterReturn;
298 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000299 }
reed@google.comdab9b4f2012-11-19 16:45:14 +0000300 patch->fMask.fBounds.offsetTo(0, 0);
301 patch->fOuterRect = dstM.fBounds;
302 patch->fCenter = center;
reed@google.comd729b3e2012-11-09 14:30:48 +0000303 return kTrue_FilterReturn;
304}
305
reed@google.com30711b72012-12-18 19:18:39 +0000306void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src,
307 SkRect* dst) const {
reed@google.com9efd9a02012-01-30 15:41:43 +0000308 dst->set(src.fLeft - fRadius, src.fTop - fRadius,
309 src.fRight + fRadius, src.fBottom + fRadius);
310}
311
reed@google.com03016a32011-08-12 14:59:59 +0000312SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkFlattenableReadBuffer& buffer)
313 : SkMaskFilter(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314 fRadius = buffer.readScalar();
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000315 fBlurStyle = (SkBlurMaskFilter::BlurStyle)buffer.readInt();
316 fBlurFlags = buffer.readUInt() & SkBlurMaskFilter::kAll_BlurFlag;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 SkASSERT(fRadius >= 0);
318 SkASSERT((unsigned)fBlurStyle < SkBlurMaskFilter::kBlurStyleCount);
319}
320
djsollen@google.com54924242012-03-29 15:18:04 +0000321void SkBlurMaskFilterImpl::flatten(SkFlattenableWriteBuffer& buffer) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 this->INHERITED::flatten(buffer);
323 buffer.writeScalar(fRadius);
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000324 buffer.writeInt(fBlurStyle);
325 buffer.writeUInt(fBlurFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326}
327
reed@google.com2b75f422011-07-07 13:43:38 +0000328static const SkMaskFilter::BlurType gBlurStyle2BlurType[] = {
329 SkMaskFilter::kNormal_BlurType,
330 SkMaskFilter::kSolid_BlurType,
331 SkMaskFilter::kOuter_BlurType,
332 SkMaskFilter::kInner_BlurType,
333};
334
bungeman@google.com18ac1ba2011-10-04 15:11:38 +0000335SkMaskFilter::BlurType SkBlurMaskFilterImpl::asABlur(BlurInfo* info) const {
reed@google.com2b75f422011-07-07 13:43:38 +0000336 if (info) {
337 info->fRadius = fRadius;
338 info->fIgnoreTransform = SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag);
339 info->fHighQuality = SkToBool(fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag);
340 }
341 return gBlurStyle2BlurType[fBlurStyle];
342}
343
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +0000344#ifdef SK_DEVELOPER
345void SkBlurMaskFilterImpl::toString(SkString* str) const {
346 str->append("SkBlurMaskFilterImpl: (");
347
348 str->append("radius: ");
349 str->appendScalar(fRadius);
350 str->append(" ");
351
352 static const char* gStyleName[SkBlurMaskFilter::kBlurStyleCount] = {
353 "normal", "solid", "outer", "inner"
354 };
355
356 str->appendf("style: %s ", gStyleName[fBlurStyle]);
357 str->append("flags: (");
358 if (fBlurFlags) {
359 bool needSeparator = false;
360 SkAddFlagToString(str,
361 SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag),
362 "IgnoreXform", &needSeparator);
363 SkAddFlagToString(str,
364 SkToBool(fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag),
365 "HighQuality", &needSeparator);
366 } else {
367 str->append("None");
368 }
369 str->append("))");
370}
371#endif
372
caryclark@google.comd26147a2011-12-15 14:16:43 +0000373SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkBlurMaskFilter)
374 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl)
375SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END