blurrect.cpp revision 71a6cbfc585959738dc0b375603696ca7f60605f
1/* 2* Copyright 2012 Google Inc. 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 8#include "gm.h" 9#include "SkBlurMask.h" 10#include "SkBlurMaskFilter.h" 11#include "SkCanvas.h" 12#include "SkPath.h" 13 14#define STROKE_WIDTH SkIntToScalar(10) 15 16typedef void (*Proc)(SkCanvas*, const SkRect&, const SkPaint&); 17 18static void fill_rect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) { 19 canvas->drawRect(r, p); 20} 21 22static void draw_donut(SkCanvas* canvas, const SkRect& r, const SkPaint& p) { 23 SkRect rect; 24 SkPath path; 25 26 rect = r; 27 rect.outset(STROKE_WIDTH/2, STROKE_WIDTH/2); 28 path.addRect(rect); 29 rect = r; 30 rect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2); 31 32 path.addRect(rect); 33 path.setFillType(SkPath::kEvenOdd_FillType); 34 35 canvas->drawPath(path, p); 36} 37 38static void draw_donut_skewed(SkCanvas* canvas, const SkRect& r, const SkPaint& p) { 39 SkRect rect; 40 SkPath path; 41 42 rect = r; 43 rect.outset(STROKE_WIDTH/2, STROKE_WIDTH/2); 44 path.addRect(rect); 45 rect = r; 46 rect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2); 47 48 rect.offset(7, -7); 49 50 path.addRect(rect); 51 path.setFillType(SkPath::kEvenOdd_FillType); 52 53 canvas->drawPath(path, p); 54} 55 56#include "SkGradientShader.h" 57 58/* 59 * Spits out a dummy gradient to test blur with shader on paint 60 */ 61static SkShader* MakeRadial() { 62 SkPoint pts[2] = { 63 { 0, 0 }, 64 { SkIntToScalar(100), SkIntToScalar(100) } 65 }; 66 SkShader::TileMode tm = SkShader::kClamp_TileMode; 67 const SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, }; 68 const SkScalar pos[] = { SK_Scalar1/4, SK_Scalar1*3/4 }; 69 SkMatrix scale; 70 scale.setScale(0.5f, 0.5f); 71 scale.postTranslate(25.f, 25.f); 72 SkPoint center0, center1; 73 center0.set(SkScalarAve(pts[0].fX, pts[1].fX), 74 SkScalarAve(pts[0].fY, pts[1].fY)); 75 center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), 76 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); 77 return SkGradientShader::CreateTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7, 78 center0, (pts[1].fX - pts[0].fX) / 2, 79 colors, pos, SK_ARRAY_COUNT(colors), tm, 80 0, &scale); 81} 82 83typedef void (*PaintProc)(SkPaint*, SkScalar width); 84 85class BlurRectGM : public skiagm::GM { 86 SkAutoTUnref<SkMaskFilter> fMaskFilters[kLastEnum_SkBlurStyle + 1]; 87 SkString fName; 88 SkAlpha fAlpha; 89public: 90 BlurRectGM(const char name[], U8CPU alpha) 91 : fName(name) 92 , fAlpha(SkToU8(alpha)) { 93 } 94 95protected: 96 void onOnceBeforeDraw() override { 97 for (int i = 0; i <= kLastEnum_SkBlurStyle; ++i) { 98 fMaskFilters[i].reset(SkBlurMaskFilter::Create((SkBlurStyle)i, 99 SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(STROKE_WIDTH/2)), 100 SkBlurMaskFilter::kHighQuality_BlurFlag)); 101 } 102 } 103 104 SkString onShortName() override { 105 return fName; 106 } 107 108 SkISize onISize() override { 109 return SkISize::Make(860, 820); 110 } 111 112 void onDraw(SkCanvas* canvas) override { 113 canvas->translate(STROKE_WIDTH*3/2, STROKE_WIDTH*3/2); 114 115 SkRect r = { 0, 0, 100, 50 }; 116 SkScalar scales[] = { SK_Scalar1, 0.6f }; 117 118 for (size_t s = 0; s < SK_ARRAY_COUNT(scales); ++s) { 119 canvas->save(); 120 for (size_t f = 0; f < SK_ARRAY_COUNT(fMaskFilters); ++f) { 121 SkPaint paint; 122 paint.setMaskFilter(fMaskFilters[f]); 123 paint.setAlpha(fAlpha); 124 125 SkPaint paintWithRadial = paint; 126 paintWithRadial.setShader(MakeRadial())->unref(); 127 128 static const Proc procs[] = { 129 fill_rect, draw_donut, draw_donut_skewed 130 }; 131 132 canvas->save(); 133 canvas->scale(scales[s], scales[s]); 134 this->drawProcs(canvas, r, paint, false, procs, SK_ARRAY_COUNT(procs)); 135 canvas->translate(r.width() * 4/3, 0); 136 this->drawProcs(canvas, r, paintWithRadial, false, procs, SK_ARRAY_COUNT(procs)); 137 canvas->translate(r.width() * 4/3, 0); 138 this->drawProcs(canvas, r, paint, true, procs, SK_ARRAY_COUNT(procs)); 139 canvas->translate(r.width() * 4/3, 0); 140 this->drawProcs(canvas, r, paintWithRadial, true, procs, SK_ARRAY_COUNT(procs)); 141 canvas->restore(); 142 143 canvas->translate(0, SK_ARRAY_COUNT(procs) * r.height() * 4/3 * scales[s]); 144 } 145 canvas->restore(); 146 canvas->translate(4 * r.width() * 4/3 * scales[s], 0); 147 } 148 } 149 150private: 151 void drawProcs(SkCanvas* canvas, const SkRect& r, const SkPaint& paint, 152 bool doClip, const Proc procs[], size_t procsCount) { 153 SkAutoCanvasRestore acr(canvas, true); 154 for (size_t i = 0; i < procsCount; ++i) { 155 if (doClip) { 156 SkRect clipRect(r); 157 clipRect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2); 158 canvas->save(); 159 canvas->clipRect(r); 160 } 161 procs[i](canvas, r, paint); 162 if (doClip) { 163 canvas->restore(); 164 } 165 canvas->translate(0, r.height() * 4/3); 166 } 167 } 168private: 169 typedef GM INHERITED; 170}; 171 172 173class BlurRectDirectGM : public skiagm::GM { 174 SkString fName; 175 int fGMWidth, fGMHeight; 176 int fPadding, fMargin; 177public: 178 BlurRectDirectGM(const char name[]) 179 : fName(name), 180 fGMWidth(1200), 181 fGMHeight(1024), 182 fPadding(10), 183 fMargin(100) 184 { 185 } 186 187protected: 188 virtual SkString onShortName() { 189 return fName; 190 } 191 192 virtual SkISize onISize() { 193 return SkISize::Make(fGMWidth, fGMHeight); 194 } 195 196 virtual void onDraw(SkCanvas* canvas) { 197 const int widths[] = {25, 5, 5, 100, 150, 25}; 198 const int heights[] = {100, 100, 5, 25, 150, 25}; 199 const SkBlurStyle styles[] = {kNormal_SkBlurStyle, kInner_SkBlurStyle, kOuter_SkBlurStyle}; 200 const float radii[] = {20, 5, 10}; 201 202 canvas->translate(50,20); 203 204 int cur_x = 0; 205 int cur_y = 0; 206 207 int max_height = 0; 208 209 for (size_t i = 0 ; i < SK_ARRAY_COUNT(widths) ; i++) { 210 int width = widths[i]; 211 int height = heights[i]; 212 SkRect r; 213 r.setWH(SkIntToScalar(width), SkIntToScalar(height)); 214 SkAutoCanvasRestore autoRestore(canvas, true); 215 216 for (size_t j = 0 ; j < SK_ARRAY_COUNT(radii) ; j++) { 217 float radius = radii[j]; 218 for (size_t k = 0 ; k < SK_ARRAY_COUNT(styles) ; k++) { 219 SkBlurStyle style = styles[k]; 220 221 SkMask mask; 222 SkBlurMask::BlurRect(SkBlurMask::ConvertRadiusToSigma(radius), &mask, r, style); 223 224 SkAutoMaskFreeImage amfi(mask.fImage); 225 226 SkBitmap bm; 227 bm.installMaskPixels(mask); 228 229 if (cur_x + bm.width() >= fGMWidth - fMargin) { 230 cur_x = 0; 231 cur_y += max_height + fPadding; 232 max_height = 0; 233 } 234 235 canvas->save(); 236 canvas->translate((SkScalar)cur_x, (SkScalar)cur_y); 237 canvas->translate(-(bm.width() - r.width())/2, -(bm.height()-r.height())/2); 238 canvas->drawBitmap(bm, 0.f, 0.f, NULL); 239 canvas->restore(); 240 241 cur_x += bm.width() + fPadding; 242 if (bm.height() > max_height) 243 max_height = bm.height(); 244 } 245 } 246 } 247 } 248 249private: 250 typedef GM INHERITED; 251}; 252 253class BlurRectCompareGM : public skiagm::GM { 254 SkString fName; 255 unsigned int fRectWidth, fRectHeight; 256 SkScalar fRadius; 257 SkBlurStyle fStyle; 258public: 259 BlurRectCompareGM(const char name[], unsigned int rectWidth, 260 unsigned int rectHeight, float radius, 261 SkBlurStyle style) 262 : fName(name) 263 , fRectWidth(rectWidth) 264 , fRectHeight(rectHeight) 265 , fRadius(radius) 266 , fStyle(style) { 267 } 268 int width() const { 269 return fRectWidth; 270 } 271 int height() const { 272 return fRectHeight; 273 } 274 SkScalar radius() const { 275 return fRadius; 276 } 277 SkBlurStyle style() const { 278 return fStyle; 279 } 280 281protected: 282 virtual SkString onShortName() { 283 return fName; 284 } 285 286 virtual SkISize onISize() { 287 return SkISize::Make(640, 480); 288 } 289 290 virtual bool makeMask(SkMask *m, const SkRect&) = 0; 291 292 virtual void onDraw(SkCanvas* canvas) { 293 SkRect r; 294 r.setWH(SkIntToScalar(fRectWidth), SkIntToScalar(fRectHeight)); 295 296 SkISize canvas_size = canvas->getDeviceSize(); 297 int center_x = (canvas_size.fWidth - (int)(r.width()))/2; 298 int center_y = (canvas_size.fHeight - (int)(r.height()))/2; 299 300 SkMask mask; 301 302 if (!this->makeMask(&mask, r)) { 303 SkPaint paint; 304 r.offset( SkIntToScalar(center_x), SkIntToScalar(center_y) ); 305 canvas->drawRect(r,paint); 306 return; 307 } 308 SkAutoMaskFreeImage amfi(mask.fImage); 309 310 SkBitmap bm; 311 bm.installMaskPixels(mask); 312 313 center_x = (canvas_size.fWidth - mask.fBounds.width())/2; 314 center_y = (canvas_size.fHeight - mask.fBounds.height())/2; 315 316 canvas->drawBitmap(bm, SkIntToScalar(center_x), SkIntToScalar(center_y), NULL); 317 } 318 319private: 320 typedef GM INHERITED; 321}; 322 323class BlurRectFastGM: public BlurRectCompareGM { 324public: 325 BlurRectFastGM(const char name[], unsigned int rectWidth, 326 unsigned int rectHeight, float blurRadius, 327 SkBlurStyle style) : 328 INHERITED(name, rectWidth, rectHeight, blurRadius, style) { 329 } 330 331protected: 332 bool makeMask(SkMask *m, const SkRect& r) override { 333 return SkBlurMask::BlurRect(SkBlurMask::ConvertRadiusToSigma(this->radius()), 334 m, r, this->style()); 335 } 336private: 337 typedef BlurRectCompareGM INHERITED; 338}; 339 340class BlurRectSlowGM: public BlurRectCompareGM { 341public: 342 BlurRectSlowGM(const char name[], unsigned int rectWidth, unsigned int rectHeight, 343 float blurRadius, SkBlurStyle style) 344 : INHERITED(name, rectWidth, rectHeight, blurRadius, style) { 345 } 346 347protected: 348 bool makeMask(SkMask *m, const SkRect& r) override { 349 SkMask src; 350 r.roundOut(&src.fBounds); 351 src.fBounds.offset(-src.fBounds.fLeft, -src.fBounds.fTop); // move to origin 352 src.fFormat = SkMask::kA8_Format; 353 src.fRowBytes = src.fBounds.width(); 354 src.fImage = SkMask::AllocImage(src.computeTotalImageSize()); 355 SkAutoMaskFreeImage amfi(src.fImage); 356 357 memset(src.fImage, 0xff, src.computeTotalImageSize()); 358 359 return SkBlurMask::BoxBlur(m, src, 360 SkBlurMask::ConvertRadiusToSigma(this->radius()), 361 this->style(), this->getQuality()); 362 } 363 364 virtual SkBlurQuality getQuality() { 365 return kHigh_SkBlurQuality; 366 } 367private: 368 typedef BlurRectCompareGM INHERITED; 369}; 370 371class BlurRectSlowLowGM: public BlurRectSlowGM { 372public: 373 BlurRectSlowLowGM(const char name[], unsigned int rectWidth, unsigned int rectHeight, 374 float blurRadius, SkBlurStyle style) 375 : INHERITED(name, rectWidth, rectHeight, blurRadius, style) { 376 } 377 378protected: 379 SkBlurQuality getQuality() override { 380 return kLow_SkBlurQuality; 381 } 382private: 383 typedef BlurRectSlowGM INHERITED; 384}; 385 386class BlurRectGroundTruthGM: public BlurRectCompareGM { 387public: 388 BlurRectGroundTruthGM(const char name[], unsigned int rectWidth, unsigned int rectHeight, 389 float blurRadius, SkBlurStyle style) 390 : INHERITED(name, rectWidth, rectHeight, blurRadius, style) { 391 } 392 393protected: 394 bool makeMask(SkMask *m, const SkRect& r) override { 395 SkMask src; 396 r.roundOut(&src.fBounds); 397 src.fBounds.offset(-src.fBounds.fLeft, -src.fBounds.fTop); // move to origin 398 src.fFormat = SkMask::kA8_Format; 399 src.fRowBytes = src.fBounds.width(); 400 src.fImage = SkMask::AllocImage(src.computeTotalImageSize()); 401 SkAutoMaskFreeImage amfi(src.fImage); 402 403 memset(src.fImage, 0xff, src.computeTotalImageSize()); 404 405 return SkBlurMask::BlurGroundTruth(SkBlurMask::ConvertRadiusToSigma(this->radius()), 406 m, src, this->style()); 407 } 408 409 virtual SkBlurQuality getQuality() { 410 return kHigh_SkBlurQuality; 411 } 412private: 413 typedef BlurRectCompareGM INHERITED; 414}; 415 416 417////////////////////////////////////////////////////////////////////////////// 418 419DEF_GM(return new BlurRectGM("blurrects", 0xFF);) 420DEF_GM(return new BlurRectDirectGM("blurrect_gallery");) 421