diff options
author | Torne (Richard Coles) <torne@google.com> | 2013-05-13 16:52:09 +0100 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2013-05-13 16:52:09 +0100 |
commit | b2df76ea8fec9e32f6f3718986dba0d95315b29c (patch) | |
tree | 0182623e854b794f7307829abf4be16435a0193a /skia | |
parent | 121d6d4bf5931d9b1dbc0b9a262f6c609440f6c7 (diff) | |
download | chromium_org-b2df76ea8fec9e32f6f3718986dba0d95315b29c.tar.gz |
Merge from Chromium at DEPS revision r199464
This commit was generated by merge_to_master.py.
Change-Id: Ic3d1f97a4fb4edd1e6ed66a70b5040da1622b5a9
Diffstat (limited to 'skia')
-rw-r--r-- | skia/ext/analysis_canvas.cc | 8 | ||||
-rw-r--r-- | skia/ext/analysis_canvas_unittest.cc | 8 | ||||
-rw-r--r-- | skia/ext/convolver.cc | 35 | ||||
-rw-r--r-- | skia/ext/convolver.h | 13 | ||||
-rw-r--r-- | skia/ext/convolver_unittest.cc | 53 | ||||
-rw-r--r-- | skia/ext/recursive_gaussian_convolution.cc | 267 | ||||
-rw-r--r-- | skia/ext/recursive_gaussian_convolution.h | 71 | ||||
-rw-r--r-- | skia/ext/recursive_gaussian_convolution_unittest.cc | 346 | ||||
-rw-r--r-- | skia/skia.gyp | 10 | ||||
-rw-r--r-- | skia/skia.target.darwin-arm.mk | 4 | ||||
-rw-r--r-- | skia/skia.target.darwin-x86.mk | 4 | ||||
-rw-r--r-- | skia/skia.target.linux-arm.mk | 4 | ||||
-rw-r--r-- | skia/skia.target.linux-x86.mk | 4 | ||||
-rw-r--r-- | skia/skia_test_expectations.txt | 9 |
14 files changed, 808 insertions, 28 deletions
diff --git a/skia/ext/analysis_canvas.cc b/skia/ext/analysis_canvas.cc index 467f62c421..cf13127d87 100644 --- a/skia/ext/analysis_canvas.cc +++ b/skia/ext/analysis_canvas.cc @@ -122,8 +122,7 @@ void AnalysisDevice::clear(SkColor color) { } void AnalysisDevice::drawPaint(const SkDraw&, const SkPaint& paint) { - isSolidColor_ = - (isSolidColor_ && isSolidColorPaint(paint) && paint.getColor() == color_); + isSolidColor_ = false; isTransparent_ = false; } @@ -168,12 +167,9 @@ void AnalysisDevice::drawRect(const SkDraw& draw, const SkRect& rect, // - We're not in "forced not solid" mode // - Paint is solid color // - The quad is a full tile quad - // - The exception is if the tile is already solid tile, - // and we're drawing the same solid color paint then - // the tile remains solid. if (!isForcedNotSolid_ && isSolidColorPaint(paint) && - (doesCoverCanvas || (isSolidColor_ && paint.getColor() == color_))) { + doesCoverCanvas) { isSolidColor_ = true; color_ = paint.getColor(); hasText_ = false; diff --git a/skia/ext/analysis_canvas_unittest.cc b/skia/ext/analysis_canvas_unittest.cc index 4c54dd5b39..d271a9ecec 100644 --- a/skia/ext/analysis_canvas_unittest.cc +++ b/skia/ext/analysis_canvas_unittest.cc @@ -179,17 +179,9 @@ TEST(AnalysisCanvasTest, SimpleDrawRect) { EXPECT_NE(static_cast<SkColor>(SK_ColorTRANSPARENT), outputColor); EXPECT_EQ(color, outputColor); - // Paint with the same color, tile should remain solid. canvas.rotate(50); canvas.drawRect(SkRect::MakeWH(255, 255), paint); - EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); - EXPECT_EQ(color, outputColor); - - color = SkColorSetARGB(255, 12, 23, 34); - paint.setColor(color); - paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); - canvas.drawRect(SkRect::MakeWH(255, 255), paint); EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); } diff --git a/skia/ext/convolver.cc b/skia/ext/convolver.cc index 2057b2219a..a7824aafb1 100644 --- a/skia/ext/convolver.cc +++ b/skia/ext/convolver.cc @@ -672,4 +672,39 @@ void SingleChannelConvolveY1D(const unsigned char* source_data, } } +void SetUpGaussianConvolutionKernel(ConvolutionFilter1D* filter, + float kernel_sigma, + bool derivative) { + DCHECK(filter != NULL); + DCHECK_GT(kernel_sigma, 0.0); + const int tail_length = static_cast<int>(4.0f * kernel_sigma + 0.5f); + const int kernel_size = tail_length * 2 + 1; + const float sigmasq = kernel_sigma * kernel_sigma; + std::vector<float> kernel_weights(kernel_size, 0.0); + float kernel_sum = 1.0f; + + kernel_weights[tail_length] = 1.0f; + + for (int ii = 1; ii <= tail_length; ++ii) { + float v = std::exp(-0.5f * ii * ii / sigmasq); + kernel_weights[tail_length + ii] = v; + kernel_weights[tail_length - ii] = v; + kernel_sum += 2.0f * v; + } + + for (int i = 0; i < kernel_size; ++i) + kernel_weights[i] /= kernel_sum; + + if (derivative) { + kernel_weights[tail_length] = 0.0; + for (int ii = 1; ii <= tail_length; ++ii) { + float v = sigmasq * kernel_weights[tail_length + ii] / ii; + kernel_weights[tail_length + ii] = v; + kernel_weights[tail_length - ii] = -v; + } + } + + filter->AddFilter(0, &kernel_weights[0], kernel_weights.size()); +} + } // namespace skia diff --git a/skia/ext/convolver.h b/skia/ext/convolver.h index 6da703c7ed..4248bc6bf0 100644 --- a/skia/ext/convolver.h +++ b/skia/ext/convolver.h @@ -115,9 +115,9 @@ class ConvolutionFilter1D { // There will be |filter_length| values in the return array. // Returns NULL if the filter is 0-length (for instance when all floating // point values passed to AddFilter were clipped to 0). - const Fixed* GetSingleFilter(int* specified_filter_length, - int* filter_offset, - int* filter_length) const; + SK_API const Fixed* GetSingleFilter(int* specified_filter_length, + int* filter_offset, + int* filter_length) const; inline void PaddingForSIMD() { // Padding |padding_count| of more dummy coefficients after the coefficients @@ -219,6 +219,13 @@ SK_API void SingleChannelConvolveY1D(const unsigned char* source_data, int output_channel_count, bool absolute_values); +// Set up the |filter| instance with a gaussian kernel. |kernel_sigma| is the +// parameter of gaussian. If |derivative| is true, the kernel will be that of +// the first derivative. Intended for use with the two routines above. +SK_API void SetUpGaussianConvolutionKernel(ConvolutionFilter1D* filter, + float kernel_sigma, + bool derivative); + } // namespace skia #endif // SKIA_EXT_CONVOLVER_H_ diff --git a/skia/ext/convolver_unittest.cc b/skia/ext/convolver_unittest.cc index 2ab61e3a47..9e59a8cef2 100644 --- a/skia/ext/convolver_unittest.cc +++ b/skia/ext/convolver_unittest.cc @@ -4,6 +4,8 @@ #include <string.h> #include <time.h> +#include <algorithm> +#include <numeric> #include <vector> #include "base/basictypes.h" @@ -473,4 +475,55 @@ TEST(Convolver, SeparableSingleConvolutionEdges) { EXPECT_NEAR(output[test_column + dest_row_stride * (kImgHeight - 4)], 40, 1); } +TEST(Convolver, SetUpGaussianConvolutionFilter) { + ConvolutionFilter1D smoothing_filter; + ConvolutionFilter1D gradient_filter; + SetUpGaussianConvolutionKernel(&smoothing_filter, 4.5f, false); + SetUpGaussianConvolutionKernel(&gradient_filter, 3.0f, true); + + int specified_filter_length; + int filter_offset; + int filter_length; + + const ConvolutionFilter1D::Fixed* smoothing_kernel = + smoothing_filter.GetSingleFilter( + &specified_filter_length, &filter_offset, &filter_length); + EXPECT_TRUE(smoothing_kernel); + std::vector<float> fp_smoothing_kernel(filter_length); + std::transform(smoothing_kernel, + smoothing_kernel + filter_length, + fp_smoothing_kernel.begin(), + ConvolutionFilter1D::FixedToFloat); + // Should sum-up to 1 (nearly), and all values whould be in ]0, 1[. + EXPECT_NEAR(std::accumulate( + fp_smoothing_kernel.begin(), fp_smoothing_kernel.end(), 0.0f), + 1.0f, 0.01f); + EXPECT_GT(*std::min_element(fp_smoothing_kernel.begin(), + fp_smoothing_kernel.end()), 0.0f); + EXPECT_LT(*std::max_element(fp_smoothing_kernel.begin(), + fp_smoothing_kernel.end()), 1.0f); + + const ConvolutionFilter1D::Fixed* gradient_kernel = + gradient_filter.GetSingleFilter( + &specified_filter_length, &filter_offset, &filter_length); + EXPECT_TRUE(gradient_kernel); + std::vector<float> fp_gradient_kernel(filter_length); + std::transform(gradient_kernel, + gradient_kernel + filter_length, + fp_gradient_kernel.begin(), + ConvolutionFilter1D::FixedToFloat); + // Should sum-up to 0, and all values whould be in ]-1.5, 1.5[. + EXPECT_NEAR(std::accumulate( + fp_gradient_kernel.begin(), fp_gradient_kernel.end(), 0.0f), + 0.0f, 0.01f); + EXPECT_GT(*std::min_element(fp_gradient_kernel.begin(), + fp_gradient_kernel.end()), -1.5f); + EXPECT_LT(*std::min_element(fp_gradient_kernel.begin(), + fp_gradient_kernel.end()), 0.0f); + EXPECT_LT(*std::max_element(fp_gradient_kernel.begin(), + fp_gradient_kernel.end()), 1.5f); + EXPECT_GT(*std::max_element(fp_gradient_kernel.begin(), + fp_gradient_kernel.end()), 0.0f); +} + } // namespace skia diff --git a/skia/ext/recursive_gaussian_convolution.cc b/skia/ext/recursive_gaussian_convolution.cc new file mode 100644 index 0000000000..32802b164c --- /dev/null +++ b/skia/ext/recursive_gaussian_convolution.cc @@ -0,0 +1,267 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <algorithm> +#include <cmath> +#include <vector> + +#include "base/logging.h" +#include "skia/ext/recursive_gaussian_convolution.h" + +namespace skia { + +namespace { + +// Takes the value produced by accumulating element-wise product of image with +// a kernel and brings it back into range. +// All of the filter scaling factors are in fixed point with kShiftBits bits of +// fractional part. +template<bool take_absolute> +inline unsigned char FloatTo8(float f) { + int a = static_cast<int>(f + 0.5f); + if (take_absolute) + a = std::abs(a); + else if (a < 0) + return 0; + if (a < 256) + return a; + return 255; +} + +template<RecursiveFilter::Order order> +inline float ForwardFilter(float in_n_1, + float in_n, + float in_n1, + const std::vector<float>& w, + int n, + const float* b) { + switch (order) { + case RecursiveFilter::FUNCTION: + return b[0] * in_n + b[1] * w[n-1] + b[2] * w[n-2] + b[3] * w[n-3]; + case RecursiveFilter::FIRST_DERIVATIVE: + return b[0] * 0.5f * (in_n1 - in_n_1) + + b[1] * w[n-1] + b[2] * w[n-2] + b[3] * w[n-3]; + case RecursiveFilter::SECOND_DERIVATIVE: + return b[0] * (in_n - in_n_1) + + b[1] * w[n-1] + b[2] * w[n-2] + b[3] * w[n-3]; + } + + NOTREACHED(); + return 0.0f; +} + +template<RecursiveFilter::Order order> +inline float BackwardFilter(const std::vector<float>& out, + int n, + float w_n, + float w_n1, + const float* b) { + switch (order) { + case RecursiveFilter::FUNCTION: + case RecursiveFilter::FIRST_DERIVATIVE: + return b[0] * w_n + + b[1] * out[n + 1] + b[2] * out[n + 2] + b[3] * out[n + 3]; + case RecursiveFilter::SECOND_DERIVATIVE: + return b[0] * (w_n1 - w_n) + + b[1] * out[n + 1] + b[2] * out[n + 2] + b[3] * out[n + 3]; + } + NOTREACHED(); + return 0.0f; +} + +template<RecursiveFilter::Order order, bool absolute_values> +unsigned char SingleChannelRecursiveFilter( + const unsigned char* const source_data, + int source_pixel_stride, + int source_row_stride, + int row_width, + int row_count, + unsigned char* const output, + int output_pixel_stride, + int output_row_stride, + const float* b) { + const int intermediate_buffer_size = row_width + 6; + std::vector<float> w(intermediate_buffer_size); + const unsigned char* in = source_data; + unsigned char* out = output; + unsigned char max_output = 0; + for (int r = 0; r < row_count; + ++r, in += source_row_stride, out += output_row_stride) { + // Compute forward filter. + // First initialize start of the w (temporary) vector. + w[0] = w[1] = w[2] = in[0]; + // Note that special-casing of w[3] is needed because of derivatives. + w[3] = ForwardFilter<order>( + in[0], in[0], in[source_pixel_stride], w, 3, b); + int n = 4; + int c = 1; + int byte_index = source_pixel_stride; + for (; c < row_width - 1; ++c, ++n, byte_index += source_pixel_stride) { + w[n] = ForwardFilter<order>(in[byte_index - source_pixel_stride], + in[byte_index], + in[byte_index + source_pixel_stride], + w, n, b); + } + + // The value of w corresponding to the last image pixel needs to be computed + // separately, again because of derivatives. + w[n] = ForwardFilter<order>(in[byte_index - source_pixel_stride], + in[byte_index], + in[byte_index], + w, n, b); + // Now three trailing bytes set to the same value as current w[n]. + w[n + 1] = w[n]; + w[n + 2] = w[n]; + w[n + 3] = w[n]; + + // Now apply the back filter. + float w_n1 = w[n + 1]; + int output_index = (row_width - 1) * output_pixel_stride; + for (; c >= 0; output_index -= output_pixel_stride, --c, --n) { + float w_n = BackwardFilter<order>(w, n, w[n], w_n1, b); + w_n1 = w[n]; + w[n] = w_n; + out[output_index] = FloatTo8<absolute_values>(w_n); + max_output = std::max(max_output, out[output_index]); + } + } + return max_output; +} + +unsigned char SingleChannelRecursiveFilter( + const unsigned char* const source_data, + int source_pixel_stride, + int source_row_stride, + int row_width, + int row_count, + unsigned char* const output, + int output_pixel_stride, + int output_row_stride, + const float* b, + RecursiveFilter::Order order, + bool absolute_values) { + if (absolute_values) { + switch (order) { + case RecursiveFilter::FUNCTION: + return SingleChannelRecursiveFilter<RecursiveFilter::FUNCTION, true>( + source_data, source_pixel_stride, source_row_stride, + row_width, row_count, + output, output_pixel_stride, output_row_stride, b); + case RecursiveFilter::FIRST_DERIVATIVE: + return SingleChannelRecursiveFilter< + RecursiveFilter::FIRST_DERIVATIVE, true>( + source_data, source_pixel_stride, source_row_stride, + row_width, row_count, + output, output_pixel_stride, output_row_stride, b); + case RecursiveFilter::SECOND_DERIVATIVE: + return SingleChannelRecursiveFilter< + RecursiveFilter::SECOND_DERIVATIVE, true>( + source_data, source_pixel_stride, source_row_stride, + row_width, row_count, + output, output_pixel_stride, output_row_stride, b); + } + } else { + switch (order) { + case RecursiveFilter::FUNCTION: + return SingleChannelRecursiveFilter<RecursiveFilter::FUNCTION, false>( + source_data, source_pixel_stride, source_row_stride, + row_width, row_count, + output, output_pixel_stride, output_row_stride, b); + case RecursiveFilter::FIRST_DERIVATIVE: + return SingleChannelRecursiveFilter< + RecursiveFilter::FIRST_DERIVATIVE, false>( + source_data, source_pixel_stride, source_row_stride, + row_width, row_count, + output, output_pixel_stride, output_row_stride, b); + case RecursiveFilter::SECOND_DERIVATIVE: + return SingleChannelRecursiveFilter< + RecursiveFilter::SECOND_DERIVATIVE, false>( + source_data, source_pixel_stride, source_row_stride, + row_width, row_count, + output, output_pixel_stride, output_row_stride, b); + } + } + + NOTREACHED(); + return 0; +} + +} + +float RecursiveFilter::qFromSigma(float sigma) { + DCHECK_GE(sigma, 0.5f); + if (sigma <= 2.5f) + return 3.97156f - 4.14554f * std::sqrt(1.0f - 0.26891f * sigma); + return 0.98711f * sigma - 0.96330f; +} + +void RecursiveFilter::computeCoefficients(float q, float b[4]) { + b[0] = 1.57825f + 2.44413f * q + 1.4281f * q * q + 0.422205f * q * q * q; + b[1] = 2.4413f * q + 2.85619f * q * q + 1.26661f * q * q * q; + b[2] = - 1.4281f * q * q - 1.26661f * q * q * q; + b[3] = 0.422205f * q * q * q; + + // The above is exactly like in the paper. To cut down on computations, + // we can fix up these numbers a bit now. + float b_norm = 1.0f - (b[1] + b[2] + b[3]) / b[0]; + b[1] /= b[0]; + b[2] /= b[0]; + b[3] /= b[0]; + b[0] = b_norm; +} + +RecursiveFilter::RecursiveFilter(float sigma, Order order) + : order_(order), q_(qFromSigma(sigma)) { + computeCoefficients(q_, b_); +} + +unsigned char SingleChannelRecursiveGaussianX(const unsigned char* source_data, + int source_byte_row_stride, + int input_channel_index, + int input_channel_count, + const RecursiveFilter& filter, + const SkISize& image_size, + unsigned char* output, + int output_byte_row_stride, + int output_channel_index, + int output_channel_count, + bool absolute_values) { + return SingleChannelRecursiveFilter(source_data + input_channel_index, + input_channel_count, + source_byte_row_stride, + image_size.width(), + image_size.height(), + output + output_channel_index, + output_channel_count, + output_byte_row_stride, + filter.b(), + filter.order(), + absolute_values); +} + +unsigned char SingleChannelRecursiveGaussianY(const unsigned char* source_data, + int source_byte_row_stride, + int input_channel_index, + int input_channel_count, + const RecursiveFilter& filter, + const SkISize& image_size, + unsigned char* output, + int output_byte_row_stride, + int output_channel_index, + int output_channel_count, + bool absolute_values) { + return SingleChannelRecursiveFilter(source_data + input_channel_index, + source_byte_row_stride, + input_channel_count, + image_size.height(), + image_size.width(), + output + output_channel_index, + output_byte_row_stride, + output_channel_count, + filter.b(), + filter.order(), + absolute_values); +} + +} // namespace skia diff --git a/skia/ext/recursive_gaussian_convolution.h b/skia/ext/recursive_gaussian_convolution.h new file mode 100644 index 0000000000..a4843292e1 --- /dev/null +++ b/skia/ext/recursive_gaussian_convolution.h @@ -0,0 +1,71 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SKIA_EXT_RECURSIVE_GAUSSIAN_CONVOLUTION_H_ +#define SKIA_EXT_RECURSIVE_GAUSSIAN_CONVOLUTION_H_ + +#include "skia/ext/convolver.h" +#include "third_party/skia/include/core/SkSize.h" +#include "third_party/skia/include/core/SkTypes.h" + +namespace skia { + +// RecursiveFilter, paired with SingleChannelRecursiveGaussianX and +// SingleChannelRecursiveGaussianY routines below implement recursive filters as +// described in 'Recursive implementation of the Gaussian filter' (Young, Vliet) +// (1995). Single-letter variable names mirror exactly the usage in the paper to +// ease reading and analysis. +class RecursiveFilter { + public: + enum Order { + FUNCTION, + FIRST_DERIVATIVE, + SECOND_DERIVATIVE + }; + + static float qFromSigma(float sigma); + static void computeCoefficients(float q, float b[4]); + SK_API RecursiveFilter(float sigma, Order order); + + Order order() const { return order_; } + const float* b() const { return b_; } + + private: + Order order_; + float q_; + float b_[4]; +}; + +// Applies a gaussian recursive filter given as |filter| to a single channel at +// |input_channel_index| to image given in |source_data| along X axis. +// The output is placed into |output| into channel |output_channel_index|. +SK_API unsigned char SingleChannelRecursiveGaussianX( + const unsigned char* source_data, + int source_byte_row_stride, + int input_channel_index, + int input_channel_count, + const RecursiveFilter& filter, + const SkISize& image_size, + unsigned char* output, + int output_byte_row_stride, + int output_channel_index, + int output_channel_count, + bool absolute_values); + +// Applies a gaussian recursive filter along Y axis. +SK_API unsigned char SingleChannelRecursiveGaussianY( + const unsigned char* source_data, + int source_byte_row_stride, + int input_channel_index, + int input_channel_count, + const RecursiveFilter& filter, + const SkISize& image_size, + unsigned char* output, + int output_byte_row_stride, + int output_channel_index, + int output_channel_count, + bool absolute_values); +} // namespace skia + +#endif // SKIA_EXT_RECURSIVE_GAUSSIAN_CONVOLUTION_H_ diff --git a/skia/ext/recursive_gaussian_convolution_unittest.cc b/skia/ext/recursive_gaussian_convolution_unittest.cc new file mode 100644 index 0000000000..e51bb6057c --- /dev/null +++ b/skia/ext/recursive_gaussian_convolution_unittest.cc @@ -0,0 +1,346 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <numeric> +#include <vector> + +#include "base/basictypes.h" +#include "base/file_util.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/time.h" +#include "skia/ext/convolver.h" +#include "skia/ext/recursive_gaussian_convolution.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkPoint.h" +#include "third_party/skia/include/core/SkRect.h" + +namespace { + +int ComputeRowStride(int width, int channel_count, int stride_slack) { + return width * channel_count + stride_slack; +} + +SkIPoint MakeImpulseImage(std::vector<unsigned char>* image, + int width, + int height, + int channel_index, + int channel_count, + int stride_slack) { + const int src_row_stride = ComputeRowStride( + width, channel_count, stride_slack); + const int src_byte_count = src_row_stride * height; + const int signal_x = width / 2; + const int signal_y = height / 2; + + image->resize(src_byte_count, 0); + const int non_zero_pixel_index = + signal_y * src_row_stride + signal_x * channel_count + channel_index; + (*image)[non_zero_pixel_index] = 255; + return SkIPoint::Make(signal_x, signal_y); +} + +SkIRect MakeBoxImage(std::vector<unsigned char>* image, + int width, + int height, + int channel_index, + int channel_count, + int stride_slack, + int box_width, + int box_height, + unsigned char value) { + const int src_row_stride = ComputeRowStride( + width, channel_count, stride_slack); + const int src_byte_count = src_row_stride * height; + const SkIRect box = SkIRect::MakeXYWH((width - box_width) / 2, + (height - box_height) / 2, + box_width, box_height); + + image->resize(src_byte_count, 0); + for (int y = box.top(); y < box.bottom(); ++y) { + for (int x = box.left(); x < box.right(); ++x) + (*image)[y * src_row_stride + x * channel_count + channel_index] = value; + } + + return box; +} + +int ComputeBoxSum(const std::vector<unsigned char>& image, + const SkIRect& box, + int image_width) { + // Compute the sum of all pixels in the box. Assume byte stride 1 and row + // stride same as image_width. + int sum = 0; + for (int y = box.top(); y < box.bottom(); ++y) { + for (int x = box.left(); x < box.right(); ++x) + sum += image[y * image_width + x]; + } + + return sum; +} + +} // namespace + +namespace skia { + +TEST(RecursiveGaussian, SmoothingMethodComparison) { + static const int kImgWidth = 512; + static const int kImgHeight = 220; + static const int kChannelIndex = 3; + static const int kChannelCount = 3; + static const int kStrideSlack = 22; + + std::vector<unsigned char> input; + SkISize image_size = SkISize::Make(kImgWidth, kImgHeight); + MakeImpulseImage( + &input, kImgWidth, kImgHeight, kChannelIndex, kChannelCount, + kStrideSlack); + + // Destination will be a single channel image with stide matching width. + const int dest_row_stride = kImgWidth; + const int dest_byte_count = dest_row_stride * kImgHeight; + std::vector<unsigned char> intermediate(dest_byte_count); + std::vector<unsigned char> intermediate2(dest_byte_count); + std::vector<unsigned char> control(dest_byte_count); + std::vector<unsigned char> output(dest_byte_count); + + const int src_row_stride = ComputeRowStride( + kImgWidth, kChannelCount, kStrideSlack); + + const float kernel_sigma = 2.5f; + ConvolutionFilter1D filter; + SetUpGaussianConvolutionKernel(&filter, kernel_sigma, false); + // Process the control image. + SingleChannelConvolveX1D(&input[0], src_row_stride, + kChannelIndex, kChannelCount, + filter, image_size, + &intermediate[0], dest_row_stride, 0, 1, false); + SingleChannelConvolveY1D(&intermediate[0], dest_row_stride, 0, 1, + filter, image_size, + &control[0], dest_row_stride, 0, 1, false); + + // Now try the same using the other method. + RecursiveFilter recursive_filter(kernel_sigma, RecursiveFilter::FUNCTION); + SingleChannelRecursiveGaussianY(&input[0], src_row_stride, + kChannelIndex, kChannelCount, + recursive_filter, image_size, + &intermediate2[0], dest_row_stride, + 0, 1, false); + SingleChannelRecursiveGaussianX(&intermediate2[0], dest_row_stride, 0, 1, + recursive_filter, image_size, + &output[0], dest_row_stride, 0, 1, false); + + // We cannot expect the results to be really the same. In particular, + // the standard implementation is computed in completely fixed-point, while + // recursive is done in floating point and squeezed back into char*. On top + // of that, its characteristics are a bit different (consult the paper). + EXPECT_NEAR(std::accumulate(intermediate.begin(), intermediate.end(), 0), + std::accumulate(intermediate2.begin(), intermediate2.end(), 0), + 50); + int difference_count = 0; + std::vector<unsigned char>::const_iterator i1, i2; + for (i1 = control.begin(), i2 = output.begin(); + i1 != control.end(); ++i1, ++i2) { + if ((*i1 != 0) != (*i2 != 0)) + difference_count++; + } + + EXPECT_LE(difference_count, 44); // 44 is 2 * PI * r (r == 7, spot size). +} + +TEST(RecursiveGaussian, SmoothingImpulse) { + static const int kImgWidth = 200; + static const int kImgHeight = 300; + static const int kChannelIndex = 3; + static const int kChannelCount = 3; + static const int kStrideSlack = 22; + + std::vector<unsigned char> input; + SkISize image_size = SkISize::Make(kImgWidth, kImgHeight); + const SkIPoint centre_point = MakeImpulseImage( + &input, kImgWidth, kImgHeight, kChannelIndex, kChannelCount, + kStrideSlack); + + // Destination will be a single channel image with stide matching width. + const int dest_row_stride = kImgWidth; + const int dest_byte_count = dest_row_stride * kImgHeight; + std::vector<unsigned char> intermediate(dest_byte_count); + std::vector<unsigned char> output(dest_byte_count); + + const int src_row_stride = ComputeRowStride( + kImgWidth, kChannelCount, kStrideSlack); + + const float kernel_sigma = 5.0f; + RecursiveFilter recursive_filter(kernel_sigma, RecursiveFilter::FUNCTION); + SingleChannelRecursiveGaussianY(&input[0], src_row_stride, + kChannelIndex, kChannelCount, + recursive_filter, image_size, + &intermediate[0], dest_row_stride, + 0, 1, false); + SingleChannelRecursiveGaussianX(&intermediate[0], dest_row_stride, 0, 1, + recursive_filter, image_size, + &output[0], dest_row_stride, 0, 1, false); + + // Check we got the expected impulse response. + const int cx = centre_point.x(); + const int cy = centre_point.y(); + unsigned char value_x = output[dest_row_stride * cy + cx]; + unsigned char value_y = value_x; + EXPECT_GT(value_x, 0); + for (int offset = 0; + offset < std::min(kImgWidth, kImgHeight) && (value_y > 0 || value_x > 0); + ++offset) { + // Symmetricity and monotonicity along X. + EXPECT_EQ(output[dest_row_stride * cy + cx - offset], + output[dest_row_stride * cy + cx + offset]); + EXPECT_LE(output[dest_row_stride * cy + cx - offset], value_x); + value_x = output[dest_row_stride * cy + cx - offset]; + + // Symmetricity and monotonicity along Y. + EXPECT_EQ(output[dest_row_stride * (cy - offset) + cx], + output[dest_row_stride * (cy + offset) + cx]); + EXPECT_LE(output[dest_row_stride * (cy - offset) + cx], value_y); + value_y = output[dest_row_stride * (cy - offset) + cx]; + + // Symmetricity along X/Y (not really assured, but should be close). + EXPECT_NEAR(value_x, value_y, 1); + } +} + +TEST(RecursiveGaussian, FirstDerivative) { + static const int kImgWidth = 512; + static const int kImgHeight = 1024; + static const int kChannelIndex = 2; + static const int kChannelCount = 4; + static const int kStrideSlack = 22; + static const int kBoxSize = 400; + + std::vector<unsigned char> input; + const SkISize image_size = SkISize::Make(kImgWidth, kImgHeight); + const SkIRect box = MakeBoxImage( + &input, kImgWidth, kImgHeight, kChannelIndex, kChannelCount, + kStrideSlack, kBoxSize, kBoxSize, 200); + + // Destination will be a single channel image with stide matching width. + const int dest_row_stride = kImgWidth; + const int dest_byte_count = dest_row_stride * kImgHeight; + std::vector<unsigned char> output_x(dest_byte_count); + std::vector<unsigned char> output_y(dest_byte_count); + std::vector<unsigned char> output(dest_byte_count); + + const int src_row_stride = ComputeRowStride( + kImgWidth, kChannelCount, kStrideSlack); + + const float kernel_sigma = 3.0f; + const int spread = 4 * kernel_sigma; + RecursiveFilter recursive_filter(kernel_sigma, + RecursiveFilter::FIRST_DERIVATIVE); + SingleChannelRecursiveGaussianX(&input[0], src_row_stride, + kChannelIndex, kChannelCount, + recursive_filter, image_size, + &output_x[0], dest_row_stride, + 0, 1, true); + SingleChannelRecursiveGaussianY(&input[0], src_row_stride, + kChannelIndex, kChannelCount, + recursive_filter, image_size, + &output_y[0], dest_row_stride, + 0, 1, true); + + // In test code we can assume adding the two up should do fine. + std::vector<unsigned char>::const_iterator ix, iy; + std::vector<unsigned char>::iterator target; + for (target = output.begin(), ix = output_x.begin(), iy = output_y.begin(); + target < output.end(); ++target, ++ix, ++iy) { + *target = *ix + *iy; + } + + int image_total = ComputeBoxSum(output, + SkIRect::MakeWH(kImgWidth, kImgHeight), + kImgWidth); + int box_inflated = ComputeBoxSum(output, + SkIRect::MakeLTRB(box.left() - spread, + box.top() - spread, + box.right() + spread, + box.bottom() + spread), + kImgWidth); + int box_deflated = ComputeBoxSum(output, + SkIRect::MakeLTRB(box.left() + spread, + box.top() + spread, + box.right() - spread, + box.bottom() - spread), + kImgWidth); + EXPECT_EQ(box_deflated, 0); + EXPECT_EQ(image_total, box_inflated); +} + +TEST(RecursiveGaussian, SecondDerivative) { + static const int kImgWidth = 700; + static const int kImgHeight = 500; + static const int kChannelIndex = 0; + static const int kChannelCount = 2; + static const int kStrideSlack = 42; + static const int kBoxSize = 200; + + std::vector<unsigned char> input; + SkISize image_size = SkISize::Make(kImgWidth, kImgHeight); + const SkIRect box = MakeBoxImage( + &input, kImgWidth, kImgHeight, kChannelIndex, kChannelCount, + kStrideSlack, kBoxSize, kBoxSize, 200); + + // Destination will be a single channel image with stide matching width. + const int dest_row_stride = kImgWidth; + const int dest_byte_count = dest_row_stride * kImgHeight; + std::vector<unsigned char> output_x(dest_byte_count); + std::vector<unsigned char> output_y(dest_byte_count); + std::vector<unsigned char> output(dest_byte_count); + + const int src_row_stride = ComputeRowStride( + kImgWidth, kChannelCount, kStrideSlack); + + const float kernel_sigma = 5.0f; + const int spread = 8 * kernel_sigma; + RecursiveFilter recursive_filter(kernel_sigma, + RecursiveFilter::SECOND_DERIVATIVE); + SingleChannelRecursiveGaussianX(&input[0], src_row_stride, + kChannelIndex, kChannelCount, + recursive_filter, image_size, + &output_x[0], dest_row_stride, + 0, 1, true); + SingleChannelRecursiveGaussianY(&input[0], src_row_stride, + kChannelIndex, kChannelCount, + recursive_filter, image_size, + &output_y[0], dest_row_stride, + 0, 1, true); + + // In test code we can assume adding the two up should do fine. + std::vector<unsigned char>::const_iterator ix, iy; + std::vector<unsigned char>::iterator target; + for (target = output.begin(),ix = output_x.begin(), iy = output_y.begin(); + target < output.end(); ++target, ++ix, ++iy) { + *target = *ix + *iy; + } + + int image_total = ComputeBoxSum(output, + SkIRect::MakeWH(kImgWidth, kImgHeight), + kImgWidth); + int box_inflated = ComputeBoxSum(output, + SkIRect::MakeLTRB(box.left() - spread, + box.top() - spread, + box.right() + spread, + box.bottom() + spread), + kImgWidth); + int box_deflated = ComputeBoxSum(output, + SkIRect::MakeLTRB(box.left() + spread, + box.top() + spread, + box.right() - spread, + box.bottom() - spread), + kImgWidth); + // Since second derivative is not really used and implemented mostly + // for the sake of completeness, we do not verify the detail (that dip + // in the middle). But it is there. + EXPECT_EQ(box_deflated, 0); + EXPECT_EQ(image_total, box_inflated); +} + +} // namespace skia diff --git a/skia/skia.gyp b/skia/skia.gyp index 63ab0699bc..6fea4b7cf8 100644 --- a/skia/skia.gyp +++ b/skia/skia.gyp @@ -14,12 +14,6 @@ }, { 'skia_support_gpu': 1, }], - - ['inside_chromium_build==0', { - 'webkit_src_dir': '<(DEPTH)/../../..', - },{ - 'webkit_src_dir': '<(DEPTH)/third_party/WebKit', - }], ], 'optimize': 'max', @@ -184,6 +178,8 @@ 'ext/platform_device_linux.cc', 'ext/platform_device_mac.cc', 'ext/platform_device_win.cc', + 'ext/recursive_gaussian_convolution.cc', + 'ext/recursive_gaussian_convolution.h', 'ext/refptr.h', 'ext/SkMemory_new_handler.cpp', 'ext/skia_trace_shim.h', @@ -334,7 +330,7 @@ ['exclude', '_ios\\.(cc|cpp|mm?)$'], ], 'dependencies': [ - '<(webkit_src_dir)/Source/WebKit/chromium/skia_webkit.gyp:skia_webkit', + '<(DEPTH)/third_party/WebKit/Source/WebKit/chromium/skia_webkit.gyp:skia_webkit', ], }], [ 'OS != "mac"', { diff --git a/skia/skia.target.darwin-arm.mk b/skia/skia.target.darwin-arm.mk index a2607a14ed..d4711c61b2 100644 --- a/skia/skia.target.darwin-arm.mk +++ b/skia/skia.target.darwin-arm.mk @@ -39,6 +39,8 @@ $(gyp_intermediate_dir)/platform_device.cpp: $(LOCAL_PATH)/skia/ext/platform_dev mkdir -p $(@D); cp $< $@ $(gyp_intermediate_dir)/platform_device_linux.cpp: $(LOCAL_PATH)/skia/ext/platform_device_linux.cc mkdir -p $(@D); cp $< $@ +$(gyp_intermediate_dir)/recursive_gaussian_convolution.cpp: $(LOCAL_PATH)/skia/ext/recursive_gaussian_convolution.cc + mkdir -p $(@D); cp $< $@ $(gyp_intermediate_dir)/skia_utils_base.cpp: $(LOCAL_PATH)/skia/ext/skia_utils_base.cc mkdir -p $(@D); cp $< $@ $(gyp_intermediate_dir)/vector_canvas.cpp: $(LOCAL_PATH)/skia/ext/vector_canvas.cc @@ -54,6 +56,7 @@ LOCAL_GENERATED_SOURCES := \ $(gyp_intermediate_dir)/platform_canvas.cpp \ $(gyp_intermediate_dir)/platform_device.cpp \ $(gyp_intermediate_dir)/platform_device_linux.cpp \ + $(gyp_intermediate_dir)/recursive_gaussian_convolution.cpp \ $(gyp_intermediate_dir)/skia_utils_base.cpp \ $(gyp_intermediate_dir)/vector_canvas.cpp @@ -95,7 +98,6 @@ LOCAL_SRC_FILES := \ third_party/skia/src/core/SkBitmapProcShader.cpp \ third_party/skia/src/core/SkBitmapProcState.cpp \ third_party/skia/src/core/SkBitmapProcState_matrixProcs.cpp \ - third_party/skia/src/core/SkBitmapSampler.cpp \ third_party/skia/src/core/SkBitmap_scroll.cpp \ third_party/skia/src/core/SkBlitMask_D32.cpp \ third_party/skia/src/core/SkBlitRow_D16.cpp \ diff --git a/skia/skia.target.darwin-x86.mk b/skia/skia.target.darwin-x86.mk index cc6959af74..765fdad277 100644 --- a/skia/skia.target.darwin-x86.mk +++ b/skia/skia.target.darwin-x86.mk @@ -39,6 +39,8 @@ $(gyp_intermediate_dir)/platform_device.cpp: $(LOCAL_PATH)/skia/ext/platform_dev mkdir -p $(@D); cp $< $@ $(gyp_intermediate_dir)/platform_device_linux.cpp: $(LOCAL_PATH)/skia/ext/platform_device_linux.cc mkdir -p $(@D); cp $< $@ +$(gyp_intermediate_dir)/recursive_gaussian_convolution.cpp: $(LOCAL_PATH)/skia/ext/recursive_gaussian_convolution.cc + mkdir -p $(@D); cp $< $@ $(gyp_intermediate_dir)/skia_utils_base.cpp: $(LOCAL_PATH)/skia/ext/skia_utils_base.cc mkdir -p $(@D); cp $< $@ $(gyp_intermediate_dir)/vector_canvas.cpp: $(LOCAL_PATH)/skia/ext/vector_canvas.cc @@ -54,6 +56,7 @@ LOCAL_GENERATED_SOURCES := \ $(gyp_intermediate_dir)/platform_canvas.cpp \ $(gyp_intermediate_dir)/platform_device.cpp \ $(gyp_intermediate_dir)/platform_device_linux.cpp \ + $(gyp_intermediate_dir)/recursive_gaussian_convolution.cpp \ $(gyp_intermediate_dir)/skia_utils_base.cpp \ $(gyp_intermediate_dir)/vector_canvas.cpp @@ -96,7 +99,6 @@ LOCAL_SRC_FILES := \ third_party/skia/src/core/SkBitmapProcShader.cpp \ third_party/skia/src/core/SkBitmapProcState.cpp \ third_party/skia/src/core/SkBitmapProcState_matrixProcs.cpp \ - third_party/skia/src/core/SkBitmapSampler.cpp \ third_party/skia/src/core/SkBitmap_scroll.cpp \ third_party/skia/src/core/SkBlitMask_D32.cpp \ third_party/skia/src/core/SkBlitRow_D16.cpp \ diff --git a/skia/skia.target.linux-arm.mk b/skia/skia.target.linux-arm.mk index a2607a14ed..d4711c61b2 100644 --- a/skia/skia.target.linux-arm.mk +++ b/skia/skia.target.linux-arm.mk @@ -39,6 +39,8 @@ $(gyp_intermediate_dir)/platform_device.cpp: $(LOCAL_PATH)/skia/ext/platform_dev mkdir -p $(@D); cp $< $@ $(gyp_intermediate_dir)/platform_device_linux.cpp: $(LOCAL_PATH)/skia/ext/platform_device_linux.cc mkdir -p $(@D); cp $< $@ +$(gyp_intermediate_dir)/recursive_gaussian_convolution.cpp: $(LOCAL_PATH)/skia/ext/recursive_gaussian_convolution.cc + mkdir -p $(@D); cp $< $@ $(gyp_intermediate_dir)/skia_utils_base.cpp: $(LOCAL_PATH)/skia/ext/skia_utils_base.cc mkdir -p $(@D); cp $< $@ $(gyp_intermediate_dir)/vector_canvas.cpp: $(LOCAL_PATH)/skia/ext/vector_canvas.cc @@ -54,6 +56,7 @@ LOCAL_GENERATED_SOURCES := \ $(gyp_intermediate_dir)/platform_canvas.cpp \ $(gyp_intermediate_dir)/platform_device.cpp \ $(gyp_intermediate_dir)/platform_device_linux.cpp \ + $(gyp_intermediate_dir)/recursive_gaussian_convolution.cpp \ $(gyp_intermediate_dir)/skia_utils_base.cpp \ $(gyp_intermediate_dir)/vector_canvas.cpp @@ -95,7 +98,6 @@ LOCAL_SRC_FILES := \ third_party/skia/src/core/SkBitmapProcShader.cpp \ third_party/skia/src/core/SkBitmapProcState.cpp \ third_party/skia/src/core/SkBitmapProcState_matrixProcs.cpp \ - third_party/skia/src/core/SkBitmapSampler.cpp \ third_party/skia/src/core/SkBitmap_scroll.cpp \ third_party/skia/src/core/SkBlitMask_D32.cpp \ third_party/skia/src/core/SkBlitRow_D16.cpp \ diff --git a/skia/skia.target.linux-x86.mk b/skia/skia.target.linux-x86.mk index cc6959af74..765fdad277 100644 --- a/skia/skia.target.linux-x86.mk +++ b/skia/skia.target.linux-x86.mk @@ -39,6 +39,8 @@ $(gyp_intermediate_dir)/platform_device.cpp: $(LOCAL_PATH)/skia/ext/platform_dev mkdir -p $(@D); cp $< $@ $(gyp_intermediate_dir)/platform_device_linux.cpp: $(LOCAL_PATH)/skia/ext/platform_device_linux.cc mkdir -p $(@D); cp $< $@ +$(gyp_intermediate_dir)/recursive_gaussian_convolution.cpp: $(LOCAL_PATH)/skia/ext/recursive_gaussian_convolution.cc + mkdir -p $(@D); cp $< $@ $(gyp_intermediate_dir)/skia_utils_base.cpp: $(LOCAL_PATH)/skia/ext/skia_utils_base.cc mkdir -p $(@D); cp $< $@ $(gyp_intermediate_dir)/vector_canvas.cpp: $(LOCAL_PATH)/skia/ext/vector_canvas.cc @@ -54,6 +56,7 @@ LOCAL_GENERATED_SOURCES := \ $(gyp_intermediate_dir)/platform_canvas.cpp \ $(gyp_intermediate_dir)/platform_device.cpp \ $(gyp_intermediate_dir)/platform_device_linux.cpp \ + $(gyp_intermediate_dir)/recursive_gaussian_convolution.cpp \ $(gyp_intermediate_dir)/skia_utils_base.cpp \ $(gyp_intermediate_dir)/vector_canvas.cpp @@ -96,7 +99,6 @@ LOCAL_SRC_FILES := \ third_party/skia/src/core/SkBitmapProcShader.cpp \ third_party/skia/src/core/SkBitmapProcState.cpp \ third_party/skia/src/core/SkBitmapProcState_matrixProcs.cpp \ - third_party/skia/src/core/SkBitmapSampler.cpp \ third_party/skia/src/core/SkBitmap_scroll.cpp \ third_party/skia/src/core/SkBlitMask_D32.cpp \ third_party/skia/src/core/SkBlitRow_D16.cpp \ diff --git a/skia/skia_test_expectations.txt b/skia/skia_test_expectations.txt index 52a650032d..b71399ba94 100644 --- a/skia/skia_test_expectations.txt +++ b/skia/skia_test_expectations.txt @@ -53,4 +53,13 @@ crbug.com/237634 virtual/gpu/fast/canvas/image-pattern-rotate.html [ ImageOnlyFailure ] crbug.com/237634 virtual/gpu/fast/canvas/image-object-in-canvas.html [ ImageOnlyFailure ] +# Font metric calculations were fixed in r9005 +crbug.com/238630 css2.1/t100801-c548-ln-ht-02-b-ag.html [ Failure ] + +# Windows font metric handling has changed +crbug.com/239436 svg/batik/text/textEffect3.svg [ ImageOnlyFailure ] +crbug.com/239436 svg/transforms/animated-path-inside-transformed-html.xhtml [ ImageOnlyFailure ] +crbug.com/239436 svg/transforms/text-with-pattern-inside-transformed-html.xhtml [ ImageOnlyFailure ] +crbug.com/239436 svg/transforms/text-with-pattern-with-svg-transform.svg [ ImageOnlyFailure ] + # END OVERRIDES HERE (this line ensures that the file is newline-terminated) |