Add imgblur tool to assist BlurMaskFilter debugging
imgblur is intended to establish a ground truth for debugging mask blur issues. It performs a brute force (non-separable) Gaussian blur of the provided image. The blur code itself is in sk_tools_utils so it can be more easily used programmatically in other places (e.g., blur unit tests). Review URL: https://codereview.chromium.org/1384203002
This commit is contained in:
parent
b3f1636ec8
commit
9c4909b50f
@ -24,9 +24,11 @@
|
||||
'filter',
|
||||
'flatten',
|
||||
'gpuveto',
|
||||
'imgblur',
|
||||
'imgconv',
|
||||
'imgslice',
|
||||
'lua_app',
|
||||
'lua_pictures',
|
||||
'imgconv',
|
||||
'pinspect',
|
||||
'render_pdfs',
|
||||
'render_pictures',
|
||||
@ -36,7 +38,6 @@
|
||||
'skpdiff',
|
||||
'skpinfo',
|
||||
'skpmaker',
|
||||
'imgslice',
|
||||
'test_image_decoder',
|
||||
'test_public_includes',
|
||||
'whitelist_typefaces',
|
||||
@ -312,6 +313,22 @@
|
||||
'skia_lib.gyp:skia_lib',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'imgblur',
|
||||
'type': 'executable',
|
||||
'sources': [
|
||||
'../tools/imgblur.cpp',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../include/core',
|
||||
],
|
||||
'dependencies': [
|
||||
'flags.gyp:flags',
|
||||
'flags.gyp:flags_common',
|
||||
'skia_lib.gyp:skia_lib',
|
||||
'tools.gyp:sk_tool_utils',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'imgslice',
|
||||
'type': 'executable',
|
||||
|
83
tools/imgblur.cpp
Normal file
83
tools/imgblur.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkCommandLineFlags.h"
|
||||
#include "SkCommonFlags.h"
|
||||
#include "SkImageDecoder.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkTypes.h"
|
||||
|
||||
#include "sk_tool_utils.h"
|
||||
|
||||
DEFINE_string(in, "input.png", "Input image");
|
||||
DEFINE_string(out, "blurred.png", "Output image");
|
||||
DEFINE_double(sigma, 1, "Sigma to be used for blur (> 0.0f)");
|
||||
|
||||
|
||||
// This tool just performs a blur on an input image
|
||||
// Return codes:
|
||||
static const int kSuccess = 0;
|
||||
static const int kError = 1;
|
||||
|
||||
int tool_main(int argc, char** argv);
|
||||
int tool_main(int argc, char** argv) {
|
||||
SkCommandLineFlags::SetUsage("Brute force blur of an image.");
|
||||
SkCommandLineFlags::Parse(argc, argv);
|
||||
|
||||
if (FLAGS_sigma <= 0) {
|
||||
if (!FLAGS_quiet) {
|
||||
SkDebugf("Sigma must be greater than zero (it is %f).\n", FLAGS_sigma);
|
||||
}
|
||||
return kError;
|
||||
}
|
||||
|
||||
SkFILEStream inputStream(FLAGS_in[0]);
|
||||
if (!inputStream.isValid()) {
|
||||
if (!FLAGS_quiet) {
|
||||
SkDebugf("Couldn't open file: %s\n", FLAGS_in[0]);
|
||||
}
|
||||
return kError;
|
||||
}
|
||||
|
||||
SkAutoTDelete<SkImageDecoder> codec(SkImageDecoder::Factory(&inputStream));
|
||||
if (!codec) {
|
||||
if (!FLAGS_quiet) {
|
||||
SkDebugf("Couldn't create codec for: %s.\n", FLAGS_in[0]);
|
||||
}
|
||||
return kError;
|
||||
}
|
||||
|
||||
SkBitmap src;
|
||||
|
||||
inputStream.rewind();
|
||||
SkImageDecoder::Result res = codec->decode(&inputStream, &src,
|
||||
kN32_SkColorType,
|
||||
SkImageDecoder::kDecodePixels_Mode);
|
||||
if (SkImageDecoder::kSuccess != res) {
|
||||
if (!FLAGS_quiet) {
|
||||
SkDebugf("Couldn't decode image: %s.\n", FLAGS_in[0]);
|
||||
}
|
||||
return kError;
|
||||
}
|
||||
|
||||
SkBitmap dst = sk_tool_utils::slow_blur(src, (float) FLAGS_sigma);
|
||||
|
||||
if (!SkImageEncoder::EncodeFile(FLAGS_out[0], dst, SkImageEncoder::kPNG_Type, 100)) {
|
||||
if (!FLAGS_quiet) {
|
||||
SkDebugf("Couldn't write to file: %s\n", FLAGS_out[0]);
|
||||
}
|
||||
return kError;
|
||||
}
|
||||
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
#if !defined SK_BUILD_FOR_IOS
|
||||
int main(int argc, char * const argv[]) {
|
||||
return tool_main(argc, (char**) argv);
|
||||
}
|
||||
#endif
|
@ -349,4 +349,98 @@ void make_big_path(SkPath& path) {
|
||||
#include "BigPathBench.inc"
|
||||
}
|
||||
|
||||
static float gaussian2d_value(int x, int y, float sigma) {
|
||||
// don't bother with the scale term since we're just going to normalize the
|
||||
// kernel anyways
|
||||
float temp = exp(-(x*x + y*y)/(2*sigma*sigma));
|
||||
return temp;
|
||||
}
|
||||
|
||||
static float* create_2d_kernel(float sigma, int* filterSize) {
|
||||
// We will actually take 2*halfFilterSize+1 samples (i.e., our filter kernel
|
||||
// sizes are always odd)
|
||||
int halfFilterSize = SkScalarCeilToInt(6*sigma)/2;
|
||||
int wh = *filterSize = 2*halfFilterSize + 1;
|
||||
|
||||
float* temp = new float[wh*wh];
|
||||
|
||||
float filterTot = 0.0f;
|
||||
for (int yOff = 0; yOff < wh; ++yOff) {
|
||||
for (int xOff = 0; xOff < wh; ++xOff) {
|
||||
temp[yOff*wh+xOff] = gaussian2d_value(xOff-halfFilterSize, yOff-halfFilterSize, sigma);
|
||||
|
||||
filterTot += temp[yOff*wh+xOff];
|
||||
}
|
||||
}
|
||||
|
||||
// normalize the kernel
|
||||
for (int yOff = 0; yOff < wh; ++yOff) {
|
||||
for (int xOff = 0; xOff < wh; ++xOff) {
|
||||
temp[yOff*wh+xOff] /= filterTot;
|
||||
}
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
static SkPMColor blur_pixel(const SkBitmap& bm, int x, int y, float* kernel, int wh) {
|
||||
SkASSERT(wh & 0x1);
|
||||
|
||||
int halfFilterSize = (wh-1)/2;
|
||||
|
||||
float r = 0.0f, g = 0.0f, b = 0.0f;
|
||||
for (int yOff = 0; yOff < wh; ++yOff) {
|
||||
int ySamp = y + yOff - halfFilterSize;
|
||||
|
||||
if (ySamp < 0) {
|
||||
ySamp = 0;
|
||||
} else if (ySamp > bm.height()-1) {
|
||||
ySamp = bm.height()-1;
|
||||
}
|
||||
|
||||
for (int xOff = 0; xOff < wh; ++xOff) {
|
||||
int xSamp = x + xOff - halfFilterSize;
|
||||
|
||||
if (xSamp < 0) {
|
||||
xSamp = 0;
|
||||
} else if (xSamp > bm.width()-1) {
|
||||
xSamp = bm.width()-1;
|
||||
}
|
||||
|
||||
float filter = kernel[yOff*wh + xOff];
|
||||
|
||||
SkPMColor c = *bm.getAddr32(xSamp, ySamp);
|
||||
|
||||
r += SkGetPackedR32(c) * filter;
|
||||
g += SkGetPackedG32(c) * filter;
|
||||
b += SkGetPackedB32(c) * filter;
|
||||
}
|
||||
}
|
||||
|
||||
U8CPU r8, g8, b8;
|
||||
|
||||
r8 = (U8CPU) (r+0.5f);
|
||||
g8 = (U8CPU) (g+0.5f);
|
||||
b8 = (U8CPU) (b+0.5f);
|
||||
|
||||
return SkPackARGB32(255, r8, g8, b8);
|
||||
}
|
||||
|
||||
SkBitmap slow_blur(const SkBitmap& src, float sigma) {
|
||||
SkBitmap dst;
|
||||
|
||||
dst.allocN32Pixels(src.width(), src.height(), true);
|
||||
|
||||
int wh;
|
||||
SkAutoTDeleteArray<float> kernel(create_2d_kernel(sigma, &wh));
|
||||
|
||||
for (int y = 0; y < src.height(); ++y) {
|
||||
for (int x = 0; x < src.width(); ++x) {
|
||||
*dst.getAddr32(x, y) = blur_pixel(src, x, y, kernel.get(), wh);
|
||||
}
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
} // namespace sk_tool_utils
|
||||
|
@ -135,6 +135,11 @@ namespace sk_tool_utils {
|
||||
void create_tetra_normal_map(SkBitmap* bm, const SkIRect& dst);
|
||||
|
||||
void make_big_path(SkPath& path);
|
||||
|
||||
// Return a blurred version of 'src'. This doesn't use a separable filter
|
||||
// so it is slow!
|
||||
SkBitmap slow_blur(const SkBitmap& src, float sigma);
|
||||
|
||||
} // namespace sk_tool_utils
|
||||
|
||||
#endif // sk_tool_utils_DEFINED
|
||||
|
Loading…
Reference in New Issue
Block a user