diff options
-rw-r--r-- | gyp/tools.gyp | 21 | ||||
-rw-r--r-- | tools/imgblur.cpp | 83 | ||||
-rw-r--r-- | tools/sk_tool_utils.cpp | 94 | ||||
-rw-r--r-- | tools/sk_tool_utils.h | 5 |
4 files changed, 201 insertions, 2 deletions
diff --git a/gyp/tools.gyp b/gyp/tools.gyp index accdd61681..8685570347 100644 --- a/gyp/tools.gyp +++ b/gyp/tools.gyp @@ -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', @@ -313,6 +314,22 @@ ], }, { + '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', 'sources': [ diff --git a/tools/imgblur.cpp b/tools/imgblur.cpp new file mode 100644 index 0000000000..5ee8b12370 --- /dev/null +++ b/tools/imgblur.cpp @@ -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 diff --git a/tools/sk_tool_utils.cpp b/tools/sk_tool_utils.cpp index 710c204271..9a667b4a45 100644 --- a/tools/sk_tool_utils.cpp +++ b/tools/sk_tool_utils.cpp @@ -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 diff --git a/tools/sk_tool_utils.h b/tools/sk_tool_utils.h index e0ad1df3ec..052bade6a7 100644 --- a/tools/sk_tool_utils.h +++ b/tools/sk_tool_utils.h @@ -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 |