summaryrefslogtreecommitdiff
path: root/skia
diff options
context:
space:
mode:
authorTorne (Richard Coles) <torne@google.com>2013-05-13 16:52:09 +0100
committerTorne (Richard Coles) <torne@google.com>2013-05-13 16:52:09 +0100
commitb2df76ea8fec9e32f6f3718986dba0d95315b29c (patch)
tree0182623e854b794f7307829abf4be16435a0193a /skia
parent121d6d4bf5931d9b1dbc0b9a262f6c609440f6c7 (diff)
downloadchromium_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.cc8
-rw-r--r--skia/ext/analysis_canvas_unittest.cc8
-rw-r--r--skia/ext/convolver.cc35
-rw-r--r--skia/ext/convolver.h13
-rw-r--r--skia/ext/convolver_unittest.cc53
-rw-r--r--skia/ext/recursive_gaussian_convolution.cc267
-rw-r--r--skia/ext/recursive_gaussian_convolution.h71
-rw-r--r--skia/ext/recursive_gaussian_convolution_unittest.cc346
-rw-r--r--skia/skia.gyp10
-rw-r--r--skia/skia.target.darwin-arm.mk4
-rw-r--r--skia/skia.target.darwin-x86.mk4
-rw-r--r--skia/skia.target.linux-arm.mk4
-rw-r--r--skia/skia.target.linux-x86.mk4
-rw-r--r--skia/skia_test_expectations.txt9
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)