Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(548)

Side by Side Diff: src/effects/SkBlurMaskFilter.cpp

Issue 48623006: Add ability to ninepatch blurred rounded rectangle (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 1
2 /* 2 /*
3 * Copyright 2006 The Android Open Source Project 3 * Copyright 2006 The Android Open Source Project
4 * 4 *
5 * Use of this source code is governed by a BSD-style license that can be 5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file. 6 * found in the LICENSE file.
7 */ 7 */
8 8
9 #include "SkBlurMaskFilter.h" 9 #include "SkBlurMaskFilter.h"
10 #include "SkBlurMask.h" 10 #include "SkBlurMask.h"
11 #include "SkGpuBlurUtils.h" 11 #include "SkGpuBlurUtils.h"
12 #include "SkFlattenableBuffers.h" 12 #include "SkFlattenableBuffers.h"
13 #include "SkMaskFilter.h" 13 #include "SkMaskFilter.h"
14 #include "SkRRect.h"
14 #include "SkRTConf.h" 15 #include "SkRTConf.h"
15 #include "SkStringUtils.h" 16 #include "SkStringUtils.h"
16 #include "SkStrokeRec.h" 17 #include "SkStrokeRec.h"
17 18
18 #if SK_SUPPORT_GPU 19 #if SK_SUPPORT_GPU
19 #include "GrContext.h" 20 #include "GrContext.h"
20 #include "GrTexture.h" 21 #include "GrTexture.h"
21 #include "effects/GrSimpleTextureEffect.h" 22 #include "effects/GrSimpleTextureEffect.h"
22 #include "SkGrPixelRef.h" 23 #include "SkGrPixelRef.h"
23 #endif 24 #endif
(...skipping 21 matching lines...) Expand all
45 virtual void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE; 46 virtual void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE;
46 47
47 SkDEVCODE(virtual void toString(SkString* str) const SK_OVERRIDE;) 48 SkDEVCODE(virtual void toString(SkString* str) const SK_OVERRIDE;)
48 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl) 49 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl)
49 50
50 protected: 51 protected:
51 virtual FilterReturn filterRectsToNine(const SkRect[], int count, const SkMa trix&, 52 virtual FilterReturn filterRectsToNine(const SkRect[], int count, const SkMa trix&,
52 const SkIRect& clipBounds, 53 const SkIRect& clipBounds,
53 NinePatch*) const SK_OVERRIDE; 54 NinePatch*) const SK_OVERRIDE;
54 55
56 virtual FilterReturn filterRRectToNine(const SkRRect&, const SkMatrix&,
57 const SkIRect& clipBounds,
58 NinePatch*) const SK_OVERRIDE;
59
55 bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix, 60 bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix,
56 SkIPoint* margin, SkMask::CreateMode createMode) const; 61 SkIPoint* margin, SkMask::CreateMode createMode) const;
57 62
58 private: 63 private:
59 // To avoid unseemly allocation requests (esp. for finite platforms like 64 // To avoid unseemly allocation requests (esp. for finite platforms like
60 // handset) we limit the radius so something manageable. (as opposed to 65 // handset) we limit the radius so something manageable. (as opposed to
61 // a request like 10,000) 66 // a request like 10,000)
62 static const SkScalar kMAX_BLUR_SIGMA; 67 static const SkScalar kMAX_BLUR_SIGMA;
63 68
64 SkScalar fSigma; 69 SkScalar fSigma;
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
148 const SkMatrix& matrix, 153 const SkMatrix& matrix,
149 SkIPoint* margin, SkMask::CreateMode c reateMode) const{ 154 SkIPoint* margin, SkMask::CreateMode c reateMode) const{
150 SkScalar sigma = computeXformedSigma(matrix); 155 SkScalar sigma = computeXformedSigma(matrix);
151 156
152 return SkBlurMask::BlurRect(sigma, dst, r, (SkBlurMask::Style)fBlurStyle, 157 return SkBlurMask::BlurRect(sigma, dst, r, (SkBlurMask::Style)fBlurStyle,
153 margin, createMode); 158 margin, createMode);
154 } 159 }
155 160
156 #include "SkCanvas.h" 161 #include "SkCanvas.h"
157 162
158 static bool drawRectsIntoMask(const SkRect rects[], int count, SkMask* mask) { 163 static SkCanvas* prepare_to_draw_into_mask(const SkRect& bounds, SkMask* mask) {
159 rects[0].roundOut(&mask->fBounds); 164 SkASSERT(mask != NULL);
165
166 bounds.roundOut(&mask->fBounds);
160 mask->fRowBytes = SkAlign4(mask->fBounds.width()); 167 mask->fRowBytes = SkAlign4(mask->fBounds.width());
161 mask->fFormat = SkMask::kA8_Format; 168 mask->fFormat = SkMask::kA8_Format;
162 size_t size = mask->computeImageSize(); 169 const size_t size = mask->computeImageSize();
163 mask->fImage = SkMask::AllocImage(size); 170 mask->fImage = SkMask::AllocImage(size);
164 if (NULL == mask->fImage) { 171 if (NULL == mask->fImage) {
165 return false; 172 return NULL;
166 } 173 }
174
175 // FIXME: use sk_calloc in AllocImage?
167 sk_bzero(mask->fImage, size); 176 sk_bzero(mask->fImage, size);
168
169 SkBitmap bitmap; 177 SkBitmap bitmap;
170 bitmap.setConfig(SkBitmap::kA8_Config, 178 bitmap.setConfig(SkBitmap::kA8_Config,
171 mask->fBounds.width(), mask->fBounds.height(), 179 mask->fBounds.width(), mask->fBounds.height(),
172 mask->fRowBytes); 180 mask->fRowBytes);
173 bitmap.setPixels(mask->fImage); 181 bitmap.setPixels(mask->fImage);
174 182
175 SkCanvas canvas(bitmap); 183 SkCanvas* canvas = SkNEW_ARGS(SkCanvas, (bitmap));
scroggo 2013/10/30 22:51:31 In trying to share code, I've added an allocation!
robertphillips 2013/10/31 00:24:58 Can you just set up the mask then return true or f
scroggo 2013/11/01 21:45:46 I could, but that means I'm still duplicating code
176 canvas.translate(-SkIntToScalar(mask->fBounds.left()), 184 canvas->translate(-SkIntToScalar(mask->fBounds.left()),
177 -SkIntToScalar(mask->fBounds.top())); 185 -SkIntToScalar(mask->fBounds.top()));
186 return canvas;
187 }
188
189 static bool draw_rrect_into_mask(const SkRRect rrect, SkMask* mask) {
190 SkAutoTUnref<SkCanvas> canvas(prepare_to_draw_into_mask(rrect.rect(), mask)) ;
191 if (NULL == canvas.get()) {
192 return false;
193 }
194
195 SkPaint paint;
196 paint.setAntiAlias(true);
197 canvas->drawRRect(rrect, paint);
198 return true;
199 }
200
201 static bool drawRectsIntoMask(const SkRect rects[], int count, SkMask* mask) {
202 SkAutoTUnref<SkCanvas> canvas(prepare_to_draw_into_mask(rects[0], mask));
203 if (NULL == canvas.get()) {
204 return false;
205 }
178 206
179 SkPaint paint; 207 SkPaint paint;
180 paint.setAntiAlias(true); 208 paint.setAntiAlias(true);
181 209
182 if (1 == count) { 210 if (1 == count) {
183 canvas.drawRect(rects[0], paint); 211 canvas->drawRect(rects[0], paint);
184 } else { 212 } else {
185 // todo: do I need a fast way to do this? 213 // todo: do I need a fast way to do this?
186 SkPath path; 214 SkPath path;
187 path.addRect(rects[0]); 215 path.addRect(rects[0]);
188 path.addRect(rects[1]); 216 path.addRect(rects[1]);
189 path.setFillType(SkPath::kEvenOdd_FillType); 217 path.setFillType(SkPath::kEvenOdd_FillType);
190 canvas.drawPath(path, paint); 218 canvas->drawPath(path, paint);
191 } 219 }
192 return true; 220 return true;
193 } 221 }
194 222
195 static bool rect_exceeds(const SkRect& r, SkScalar v) { 223 static bool rect_exceeds(const SkRect& r, SkScalar v) {
196 return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v || 224 return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v ||
197 r.width() > v || r.height() > v; 225 r.width() > v || r.height() > v;
198 } 226 }
199 227
228 SkMaskFilter::FilterReturn
229 SkBlurMaskFilterImpl::filterRRectToNine(const SkRRect& rrect, const SkMatrix& ma trix,
230 const SkIRect& clipBounds,
231 NinePatch* patch) const {
232 switch (rrect.getType()) {
233 case SkRRect::kUnknown_Type:
234 // Unknown should never be returned.
235 SkASSERT(false);
236 // Fall through.
237 case SkRRect::kEmpty_Type:
238 // Nothing to draw.
239 return kFalse_FilterReturn;
240
241 case SkRRect::kRect_Type:
robertphillips 2013/10/31 00:24:58 I think we should assert here (that the RRect isn'
scroggo 2013/11/01 21:45:46 Done.
242 // Fall through.
243 case SkRRect::kOval_Type:
244 // The nine patch special case does not handle ovals, and we
245 // already have code for rectangles.
246 return kUnimplemented_FilterReturn;
247
248 case SkRRect::kSimple_Type:
249 // Fall through.
250 case SkRRect::kComplex_Type:
251 // These can take advantage of this fast path.
252 break;
253 }
254
255 // TODO: report correct metrics for innerstyle, where we do not grow the
256 // total bounds, but we do need an inset the size of our blur-radius
257 if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle) {
258 return kUnimplemented_FilterReturn;
259 }
260
261 // TODO: take clipBounds into account to limit our coordinates up front
262 // for now, just skip too-large src rects (to take the old code path).
263 if (rect_exceeds(rrect.rect(), SkIntToScalar(32767))) {
264 return kUnimplemented_FilterReturn;
265 }
266
267 SkIPoint margin;
268 SkMask srcM, dstM;
269 rrect.rect().roundOut(&srcM.fBounds);
270 srcM.fImage = NULL;
271 srcM.fFormat = SkMask::kA8_Format;
272 srcM.fRowBytes = 0;
273
274 if (!this->filterMask(&dstM, srcM, matrix, &margin)) {
275 return kFalse_FilterReturn;
276 }
277
robertphillips 2013/10/31 13:30:31 Does this behave correctly if the RR is 100x100 wi
scroggo 2013/11/01 21:45:46 No. Latest patch fixes this. Thank you for the sug
278 // Now we figure out the appropriate width and height of the stretchy
279 // rectangle.
280 const SkVector& UL = rrect.radii(SkRRect::kUpperLeft_Corner);
281 const SkVector& UR = rrect.radii(SkRRect::kUpperRight_Corner);
282 const SkVector& LR = rrect.radii(SkRRect::kLowerRight_Corner);
283 const SkVector& LL = rrect.radii(SkRRect::kLowerLeft_Corner);
284
285 // The smaller rectangle must be large enough to include the larger
286 // radius of all four sides, plus some extra for stretching.
287 const SkScalar maxLeftXRadius = SkTMax(UL.fX, LL.fX);
288 const SkScalar maxRightXRadius = SkTMax(UR.fX, LR.fX);
289
robertphillips 2013/10/31 00:24:58 I think there should be a +3 in here and maybe mak
scroggo 2013/11/01 21:45:46 Done.
290 if (maxLeftXRadius + maxRightXRadius >= rrect.rect().width()) {
291 // There is no valid piece to stretch.
292 return kUnimplemented_FilterReturn;
293 }
294
295 const SkScalar maxTopYRadius = SkTMax(UL.fY, UR.fY);
296 const SkScalar maxBottomYRadius = SkTMax(LL.fY, LR.fY);
297
robertphillips 2013/10/31 00:24:58 Here too
scroggo 2013/11/01 21:45:46 Done.
298 if (maxTopYRadius + maxBottomYRadius >= rrect.rect().height()) {
299 // There is no valid piece to stretch.
300 return kUnimplemented_FilterReturn;
301 }
302
303 // Add some extra space to make sure we have a stretch row/col
robertphillips 2013/10/31 00:24:58 Are the x & y values correct? Can they be 0 & 0?
scroggo 2013/11/01 21:45:46 The x and y values are irrelevant. I have switched
304 SkRect smallR = SkRect::MakeXYWH(rrect.rect().left(), rrect.rect().top(),
305 maxLeftXRadius + maxRightXRadius + SkIntToS calar(3),
306 maxTopYRadius + maxBottomYRadius + SkIntToS calar(3));
307
308 SkRRect smallRR;
robertphillips 2013/10/31 00:24:58 Maybe change setRectRadii to setAllRadii?
scroggo 2013/11/01 21:45:46 I think the current name makes sense. It sets both
309 smallRR.setRectRadii(smallR, rrect.getAllRadii());
310
311 if (!draw_rrect_into_mask(smallRR, &srcM)) {
312 return kFalse_FilterReturn;
313 }
314
315 if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
316 return kFalse_FilterReturn;
317 }
318
319 patch->fMask.fBounds.offsetTo(0, 0);
320 patch->fOuterRect = dstM.fBounds;
321 // Stretch just beyond the radii.
322 patch->fCenter.fX = SkScalarCeilToInt(maxLeftXRadius) + 1;
323 patch->fCenter.fY = SkScalarCeilToInt(maxTopYRadius) + 1;
324 return kTrue_FilterReturn;
325 }
326
200 #ifdef SK_IGNORE_FAST_RECT_BLUR 327 #ifdef SK_IGNORE_FAST_RECT_BLUR
201 SK_CONF_DECLARE( bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch", false, "Use the faster analytic blur approach for ninepatch rects" ); 328 SK_CONF_DECLARE( bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch", false, "Use the faster analytic blur approach for ninepatch rects" );
202 #else 329 #else
203 SK_CONF_DECLARE( bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch", true, "Use the faster analytic blur approach for ninepatch rects" ); 330 SK_CONF_DECLARE( bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch", true, "Use the faster analytic blur approach for ninepatch rects" );
204 #endif 331 #endif
205 332
206 SkMaskFilter::FilterReturn 333 SkMaskFilter::FilterReturn
207 SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count, 334 SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count,
208 const SkMatrix& matrix, 335 const SkMatrix& matrix,
209 const SkIRect& clipBounds, 336 const SkIRect& clipBounds,
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after
473 } else { 600 } else {
474 str->append("None"); 601 str->append("None");
475 } 602 }
476 str->append("))"); 603 str->append("))");
477 } 604 }
478 #endif 605 #endif
479 606
480 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkBlurMaskFilter) 607 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkBlurMaskFilter)
481 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl) 608 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl)
482 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END 609 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698