OLD | NEW |
1 | 1 |
2 /* | 2 /* |
3 * Copyright 2011 Google Inc. | 3 * Copyright 2011 Google Inc. |
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 #include "Test.h" | 8 #include "Test.h" |
| 9 #include "SkBlurMask.h" |
9 #include "SkBlurMaskFilter.h" | 10 #include "SkBlurMaskFilter.h" |
10 #include "SkCanvas.h" | 11 #include "SkCanvas.h" |
11 #include "SkMath.h" | 12 #include "SkMath.h" |
12 #include "SkPaint.h" | 13 #include "SkPaint.h" |
| 14 #if SK_SUPPORT_GPU |
| 15 #include "GrContextFactory.h" |
| 16 #include "SkGpuDevice.h" |
| 17 #endif |
| 18 |
| 19 #define WRITE_CSV 0 |
13 | 20 |
14 /////////////////////////////////////////////////////////////////////////////// | 21 /////////////////////////////////////////////////////////////////////////////// |
15 | 22 |
16 #define ILLEGAL_MODE ((SkXfermode::Mode)-1) | 23 #define ILLEGAL_MODE ((SkXfermode::Mode)-1) |
17 | 24 |
18 static const int outset = 100; | 25 static const int outset = 100; |
19 static const SkColor bgColor = SK_ColorWHITE; | 26 static const SkColor bgColor = SK_ColorWHITE; |
20 static const int strokeWidth = 4; | 27 static const int strokeWidth = 4; |
21 | 28 |
22 static void create(SkBitmap* bm, SkIRect bound, SkBitmap::Config config) { | 29 static void create(SkBitmap* bm, SkIRect bound, SkBitmap::Config config) { |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
81 refColor = bgColor; | 88 refColor = bgColor; |
82 } | 89 } |
83 if (refColor != testColor) { | 90 if (refColor != testColor) { |
84 return false; | 91 return false; |
85 } | 92 } |
86 } | 93 } |
87 } | 94 } |
88 return true; | 95 return true; |
89 } | 96 } |
90 | 97 |
91 static void test_blur(skiatest::Reporter* reporter) { | 98 static void test_blur_drawing(skiatest::Reporter* reporter) { |
92 | 99 |
93 SkPaint paint; | 100 SkPaint paint; |
94 paint.setColor(SK_ColorGRAY); | 101 paint.setColor(SK_ColorGRAY); |
95 paint.setStyle(SkPaint::kStroke_Style); | 102 paint.setStyle(SkPaint::kStroke_Style); |
96 paint.setStrokeWidth(SkIntToScalar(strokeWidth)); | 103 paint.setStrokeWidth(SkIntToScalar(strokeWidth)); |
97 | 104 |
98 SkScalar radius = SkIntToScalar(5); | 105 SkScalar radius = SkIntToScalar(5); |
99 for (int style = 0; style < SkBlurMaskFilter::kBlurStyleCount; ++style) { | 106 for (int style = 0; style < SkBlurMaskFilter::kBlurStyleCount; ++style) { |
100 SkBlurMaskFilter::BlurStyle blurStyle = | 107 SkBlurMaskFilter::BlurStyle blurStyle = |
101 static_cast<SkBlurMaskFilter::BlurStyle>(style); | 108 static_cast<SkBlurMaskFilter::BlurStyle>(style); |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
138 testCanvas.drawPath(path, paint); | 145 testCanvas.drawPath(path, paint); |
139 | 146 |
140 REPORTER_ASSERT(reporter, | 147 REPORTER_ASSERT(reporter, |
141 compare(refBitmap, iref, testBitmap, itest)); | 148 compare(refBitmap, iref, testBitmap, itest)); |
142 } | 149 } |
143 } | 150 } |
144 } | 151 } |
145 } | 152 } |
146 } | 153 } |
147 | 154 |
| 155 /////////////////////////////////////////////////////////////////////////////// |
| 156 |
| 157 // Use SkBlurMask::BlurGroundTruth to blur a 'width' x 'height' solid |
| 158 // white rect. Return the right half of the middle row in 'result'. |
| 159 static void ground_truth_2d(int width, int height, |
| 160 SkScalar sigma, |
| 161 int* result, int resultCount) { |
| 162 SkMask src, dst; |
| 163 |
| 164 src.fBounds.set(0, 0, width, height); |
| 165 src.fFormat = SkMask::kA8_Format; |
| 166 src.fRowBytes = src.fBounds.width(); |
| 167 src.fImage = SkMask::AllocImage(src.computeTotalImageSize()); |
| 168 |
| 169 memset(src.fImage, 0xff, src.computeTotalImageSize()); |
| 170 |
| 171 dst.fImage = NULL; |
| 172 SkBlurMask::BlurGroundTruth(sigma, &dst, src, SkBlurMask::kNormal_Style); |
| 173 |
| 174 int midX = dst.fBounds.centerX(); |
| 175 int midY = dst.fBounds.centerY(); |
| 176 uint8_t* bytes = dst.getAddr8(midX, midY); |
| 177 int i; |
| 178 for (i = 0; i < dst.fBounds.width()-(midX-dst.fBounds.fLeft); ++i) { |
| 179 if (i < resultCount) { |
| 180 result[i] = bytes[i]; |
| 181 } |
| 182 } |
| 183 for ( ; i < resultCount; ++i) { |
| 184 result[i] = 0; |
| 185 } |
| 186 } |
| 187 |
| 188 // Implement a step function that is 255 between min and max; 0 elsewhere. |
| 189 static int step(int x, SkScalar min, SkScalar max) { |
| 190 if (min < x && x < max) { |
| 191 return 255; |
| 192 } |
| 193 return 0; |
| 194 } |
| 195 |
| 196 // Implement a Gaussian function with 0 mean and std.dev. of 'sigma'. |
| 197 static float gaussian(int x, SkScalar sigma) { |
| 198 float k = SK_Scalar1/(sigma * sqrt(2.0f*SK_ScalarPI)); |
| 199 float exponent = -(x * x) / (2 * sigma * sigma); |
| 200 return k * exp(exponent); |
| 201 } |
| 202 |
| 203 // Perform a brute force convolution of a step function with a Gaussian. |
| 204 // Return the right half in 'result' |
| 205 static void brute_force_1d(SkScalar stepMin, SkScalar stepMax, |
| 206 SkScalar gaussianSigma, |
| 207 int* result, int resultCount) { |
| 208 |
| 209 int gaussianRange = SkScalarCeilToInt(10 * gaussianSigma); |
| 210 |
| 211 for (int i = 0; i < resultCount; ++i) { |
| 212 SkScalar sum = 0.0f; |
| 213 for (int j = -gaussianRange; j < gaussianRange; ++j) { |
| 214 sum += gaussian(j, gaussianSigma) * step(i-j, stepMin, stepMax); |
| 215 } |
| 216 |
| 217 result[i] = SkClampMax(SkClampPos(int(sum + 0.5f)), 255); |
| 218 } |
| 219 } |
| 220 |
| 221 static void blur_path(SkCanvas* canvas, const SkPath& path, |
| 222 SkScalar gaussianSigma) { |
| 223 |
| 224 SkScalar midX = path.getBounds().centerX(); |
| 225 SkScalar midY = path.getBounds().centerY(); |
| 226 |
| 227 canvas->translate(-midX, -midY); |
| 228 |
| 229 SkPaint blurPaint; |
| 230 blurPaint.setColor(SK_ColorWHITE); |
| 231 SkMaskFilter* filter = SkBlurMaskFilter::Create(SkBlurMaskFilter::kNormal_Bl
urStyle, |
| 232 gaussianSigma, |
| 233 SkBlurMaskFilter::kHighQuali
ty_BlurFlag); |
| 234 blurPaint.setMaskFilter(filter)->unref(); |
| 235 |
| 236 canvas->drawColor(SK_ColorBLACK); |
| 237 canvas->drawPath(path, blurPaint); |
| 238 } |
| 239 |
| 240 // Readback the blurred draw results from the canvas |
| 241 static void readback(SkCanvas* canvas, int* result, int resultCount) { |
| 242 SkBitmap readback; |
| 243 readback.setConfig(SkBitmap::kARGB_8888_Config, resultCount, 30); |
| 244 readback.allocPixels(); |
| 245 |
| 246 SkIRect readBackRect = { 0, 0, resultCount, 30 }; |
| 247 |
| 248 canvas->readPixels(readBackRect, &readback); |
| 249 |
| 250 readback.lockPixels(); |
| 251 SkPMColor* pixels = (SkPMColor*) readback.getAddr32(0, 15); |
| 252 |
| 253 for (int i = 0; i < resultCount; ++i) { |
| 254 result[i] = SkColorGetR(pixels[i]); |
| 255 } |
| 256 } |
| 257 |
| 258 // Draw a blurred version of the provided path. |
| 259 // Return the right half of the middle row in 'result'. |
| 260 static void cpu_blur_path(const SkPath& path, SkScalar gaussianSigma, |
| 261 int* result, int resultCount) { |
| 262 |
| 263 SkBitmap bitmap; |
| 264 bitmap.setConfig(SkBitmap::kARGB_8888_Config, resultCount, 30); |
| 265 bitmap.allocPixels(); |
| 266 SkCanvas canvas(bitmap); |
| 267 |
| 268 blur_path(&canvas, path, gaussianSigma); |
| 269 readback(&canvas, result, resultCount); |
| 270 } |
| 271 |
| 272 #if SK_SUPPORT_GPU |
| 273 static void gpu_blur_path(GrContextFactory* factory, const SkPath& path, |
| 274 SkScalar gaussianSigma, |
| 275 int* result, int resultCount) { |
| 276 |
| 277 GrContext* grContext = factory->get(GrContextFactory::kNative_GLContextType)
; |
| 278 if (NULL == grContext) { |
| 279 return; |
| 280 } |
| 281 |
| 282 GrTextureDesc desc; |
| 283 desc.fConfig = kSkia8888_GrPixelConfig; |
| 284 desc.fFlags = kRenderTarget_GrTextureFlagBit; |
| 285 desc.fWidth = resultCount; |
| 286 desc.fHeight = 30; |
| 287 desc.fSampleCnt = 0; |
| 288 |
| 289 SkAutoTUnref<GrTexture> texture(grContext->createUncachedTexture(desc, NULL,
0)); |
| 290 SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice, (grContext, texture
.get()))); |
| 291 SkCanvas canvas(device.get()); |
| 292 |
| 293 blur_path(&canvas, path, gaussianSigma); |
| 294 readback(&canvas, result, resultCount); |
| 295 } |
| 296 #endif |
| 297 |
| 298 #if WRITE_CSV |
| 299 static void write_as_csv(const char* label, SkScalar scale, int* data, int count
) { |
| 300 SkDebugf("%s_%.2f,", label, scale); |
| 301 for (int i = 0; i < count-1; ++i) { |
| 302 SkDebugf("%d,", data[i]); |
| 303 } |
| 304 SkDebugf("%d\n", data[count-1]); |
| 305 } |
| 306 #endif |
| 307 |
| 308 static bool match(int* first, int* second, int count, int tol) { |
| 309 int delta; |
| 310 for (int i = 0; i < count; ++i) { |
| 311 delta = first[i] - second[i]; |
| 312 if (delta > tol || delta < -tol) { |
| 313 return false; |
| 314 } |
| 315 } |
| 316 |
| 317 return true; |
| 318 } |
| 319 |
| 320 // Test out the normal blur style with a wide range of sigmas |
| 321 static void test_sigma_range(skiatest::Reporter* reporter, GrContextFactory* fac
tory) { |
| 322 |
| 323 static const int kSize = 100; |
| 324 |
| 325 // The geometry is offset a smidge to trigger: |
| 326 // https://code.google.com/p/chromium/issues/detail?id=282418 |
| 327 SkPath rectPath; |
| 328 rectPath.addRect(0.3f, 0.3f, 100.3f, 100.3f); |
| 329 |
| 330 SkPoint polyPts[] = { |
| 331 { 0.3f, 0.3f }, |
| 332 { 100.3f, 0.3f }, |
| 333 { 100.3f, 100.3f }, |
| 334 { 0.3f, 100.3f }, |
| 335 { 2.3f, 50.3f } // a little divet to throw off the rect special case |
| 336 }; |
| 337 SkPath polyPath; |
| 338 polyPath.addPoly(polyPts, SK_ARRAY_COUNT(polyPts), true); |
| 339 |
| 340 int rectSpecialCaseResult[kSize]; |
| 341 int generalCaseResult[kSize]; |
| 342 #if SK_SUPPORT_GPU |
| 343 int gpuResult[kSize]; |
| 344 #endif |
| 345 int groundTruthResult[kSize]; |
| 346 int bruteForce1DResult[kSize]; |
| 347 |
| 348 SkScalar sigma = SkFloatToScalar(10.0f); |
| 349 |
| 350 for (int i = 0; i < 4; ++i, sigma /= 10) { |
| 351 |
| 352 cpu_blur_path(rectPath, sigma, rectSpecialCaseResult, kSize); |
| 353 cpu_blur_path(polyPath, sigma, generalCaseResult, kSize); |
| 354 #if SK_SUPPORT_GPU |
| 355 gpu_blur_path(factory, rectPath, sigma, gpuResult, kSize); |
| 356 #endif |
| 357 ground_truth_2d(100, 100, sigma, groundTruthResult, kSize); |
| 358 brute_force_1d(-50.0f, 50.0f, sigma, bruteForce1DResult, kSize); |
| 359 |
| 360 REPORTER_ASSERT(reporter, match(rectSpecialCaseResult, bruteForce1DResul
t, kSize, 5)); |
| 361 REPORTER_ASSERT(reporter, match(generalCaseResult, bruteForce1DResult, k
Size, 15)); |
| 362 #if SK_SUPPORT_GPU |
| 363 REPORTER_ASSERT(reporter, match(gpuResult, bruteForce1DResult, kSize, 1)
); |
| 364 #endif |
| 365 REPORTER_ASSERT(reporter, match(groundTruthResult, bruteForce1DResult, k
Size, 1)); |
| 366 |
| 367 #if WRITE_CSV |
| 368 write_as_csv("RectSpecialCase", sigma, rectSpecialCaseResult, kSize); |
| 369 write_as_csv("GeneralCase", sigma, generalCaseResult, kSize); |
| 370 #if SK_SUPPORT_GPU |
| 371 write_as_csv("GPU", sigma, gpuResult, kSize); |
| 372 #endif |
| 373 write_as_csv("GroundTruth2D", sigma, groundTruthResult, kSize); |
| 374 write_as_csv("BruteForce1D", sigma, bruteForce1DResult, kSize); |
| 375 #endif |
| 376 } |
| 377 } |
| 378 |
| 379 /////////////////////////////////////////////////////////////////////////////// |
| 380 |
| 381 static void test_blur(skiatest::Reporter* reporter, GrContextFactory* factory) { |
| 382 test_blur_drawing(reporter); |
| 383 test_sigma_range(reporter, factory); |
| 384 } |
| 385 |
148 #include "TestClassDef.h" | 386 #include "TestClassDef.h" |
149 DEFINE_TESTCLASS("BlurMaskFilter", BlurTestClass, test_blur) | 387 DEFINE_GPUTESTCLASS("BlurMaskFilter", BlurTestClass, test_blur) |
OLD | NEW |