OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <string.h> | 5 #include <string.h> |
6 #include <time.h> | 6 #include <time.h> |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
| 9 #include "base/basictypes.h" |
9 #include "skia/ext/convolver.h" | 10 #include "skia/ext/convolver.h" |
10 #include "testing/gtest/include/gtest/gtest.h" | 11 #include "testing/gtest/include/gtest/gtest.h" |
11 | 12 |
12 namespace skia { | 13 namespace skia { |
13 | 14 |
14 namespace { | 15 namespace { |
15 | 16 |
16 // Fills the given filter with impulse functions for the range 0->num_entries. | 17 // Fills the given filter with impulse functions for the range 0->num_entries. |
17 void FillImpulseFilter(int num_entries, ConvolutionFilter1D* filter) { | 18 void FillImpulseFilter(int num_entries, ConvolutionFilter1D* filter) { |
18 float one = 1.0f; | 19 float one = 1.0f; |
19 for (int i = 0; i < num_entries; i++) | 20 for (int i = 0; i < num_entries; i++) |
20 filter->AddFilter(i, &one, 1); | 21 filter->AddFilter(i, &one, 1); |
21 } | 22 } |
22 | 23 |
23 // Filters the given input with the impulse function, and verifies that it | 24 // Filters the given input with the impulse function, and verifies that it |
24 // does not change. | 25 // does not change. |
25 void TestImpulseConvolution(const unsigned char* data, int width, int height) { | 26 void TestImpulseConvolution(const unsigned char* data, int width, int height) { |
26 int byte_count = width * height * 4; | 27 int byte_count = width * height * 4; |
27 | 28 |
28 ConvolutionFilter1D filter_x; | 29 ConvolutionFilter1D filter_x; |
29 FillImpulseFilter(width, &filter_x); | 30 FillImpulseFilter(width, &filter_x); |
30 | 31 |
31 ConvolutionFilter1D filter_y; | 32 ConvolutionFilter1D filter_y; |
32 FillImpulseFilter(height, &filter_y); | 33 FillImpulseFilter(height, &filter_y); |
33 | 34 |
34 std::vector<unsigned char> output; | 35 std::vector<unsigned char> output; |
35 output.resize(byte_count); | 36 output.resize(byte_count); |
36 BGRAConvolve2D(data, width * 4, true, filter_x, filter_y, &output[0]); | 37 BGRAConvolve2D(data, width * 4, true, filter_x, filter_y, |
| 38 filter_x.num_values() * 4, &output[0]); |
37 | 39 |
38 // Output should exactly match input. | 40 // Output should exactly match input. |
39 EXPECT_EQ(0, memcmp(data, &output[0], byte_count)); | 41 EXPECT_EQ(0, memcmp(data, &output[0], byte_count)); |
40 } | 42 } |
41 | 43 |
42 // Fills the destination filter with a box filter averaging every two pixels | 44 // Fills the destination filter with a box filter averaging every two pixels |
43 // to produce the output. | 45 // to produce the output. |
44 void FillBoxFilter(int size, ConvolutionFilter1D* filter) { | 46 void FillBoxFilter(int size, ConvolutionFilter1D* filter) { |
45 const float box[2] = { 0.5, 0.5 }; | 47 const float box[2] = { 0.5, 0.5 }; |
46 for (int i = 0; i < size; i++) | 48 for (int i = 0; i < size; i++) |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
96 srand(static_cast<unsigned>(time(NULL))); | 98 srand(static_cast<unsigned>(time(NULL))); |
97 for (int i = 0; i < src_byte_count; i++) | 99 for (int i = 0; i < src_byte_count; i++) |
98 input[i] = rand() * 255 / RAND_MAX; | 100 input[i] = rand() * 255 / RAND_MAX; |
99 | 101 |
100 // Compute the filters. | 102 // Compute the filters. |
101 ConvolutionFilter1D filter_x, filter_y; | 103 ConvolutionFilter1D filter_x, filter_y; |
102 FillBoxFilter(dest_width, &filter_x); | 104 FillBoxFilter(dest_width, &filter_x); |
103 FillBoxFilter(dest_height, &filter_y); | 105 FillBoxFilter(dest_height, &filter_y); |
104 | 106 |
105 // Do the convolution. | 107 // Do the convolution. |
106 BGRAConvolve2D(&input[0], src_width, true, filter_x, filter_y, &output[0]); | 108 BGRAConvolve2D(&input[0], src_width, true, filter_x, filter_y, |
| 109 filter_x.num_values() * 4, &output[0]); |
107 | 110 |
108 // Compute the expected results and check, allowing for a small difference | 111 // Compute the expected results and check, allowing for a small difference |
109 // to account for rounding errors. | 112 // to account for rounding errors. |
110 for (int y = 0; y < dest_height; y++) { | 113 for (int y = 0; y < dest_height; y++) { |
111 for (int x = 0; x < dest_width; x++) { | 114 for (int x = 0; x < dest_width; x++) { |
112 for (int channel = 0; channel < 4; channel++) { | 115 for (int channel = 0; channel < 4; channel++) { |
113 int src_offset = (y * 2 * src_row_stride + x * 2 * 4) + channel; | 116 int src_offset = (y * 2 * src_row_stride + x * 2 * 4) + channel; |
114 int value = input[src_offset] + // Top left source pixel. | 117 int value = input[src_offset] + // Top left source pixel. |
115 input[src_offset + 4] + // Top right source pixel. | 118 input[src_offset + 4] + // Top right source pixel. |
116 input[src_offset + src_row_stride] + // Lower left. | 119 input[src_offset + src_row_stride] + // Lower left. |
117 input[src_offset + src_row_stride + 4]; // Lower right. | 120 input[src_offset + src_row_stride + 4]; // Lower right. |
118 value /= 4; // Average. | 121 value /= 4; // Average. |
119 int difference = value - output[(y * dest_width + x) * 4 + channel]; | 122 int difference = value - output[(y * dest_width + x) * 4 + channel]; |
120 EXPECT_TRUE(difference >= -1 || difference <= 1); | 123 EXPECT_TRUE(difference >= -1 || difference <= 1); |
121 } | 124 } |
122 } | 125 } |
123 } | 126 } |
124 } | 127 } |
125 | 128 |
| 129 // Tests the optimization in Convolver1D::AddFilter that avoids storing |
| 130 // leading/trailing zeroes. |
| 131 TEST(Convolver, AddFilter) { |
| 132 skia::ConvolutionFilter1D filter; |
| 133 |
| 134 const skia::ConvolutionFilter1D::Fixed* values = NULL; |
| 135 int filter_offset = 0; |
| 136 int filter_length = 0; |
| 137 |
| 138 // An all-zero filter is handled correctly, all factors ignored |
| 139 static const float factors1[] = { 0.0f, 0.0f, 0.0f }; |
| 140 filter.AddFilter(11, factors1, arraysize(factors1)); |
| 141 ASSERT_EQ(0, filter.max_filter()); |
| 142 ASSERT_EQ(1, filter.num_values()); |
| 143 |
| 144 values = filter.FilterForValue(0, &filter_offset, &filter_length); |
| 145 ASSERT_TRUE(values == NULL); // No values => NULL. |
| 146 ASSERT_EQ(11, filter_offset); // Same as input offset. |
| 147 ASSERT_EQ(0, filter_length); // But no factors since all are zeroes. |
| 148 |
| 149 // Zeroes on the left are ignored |
| 150 static const float factors2[] = { 0.0f, 1.0f, 1.0f, 1.0f, 1.0f }; |
| 151 filter.AddFilter(22, factors2, arraysize(factors2)); |
| 152 ASSERT_EQ(4, filter.max_filter()); |
| 153 ASSERT_EQ(2, filter.num_values()); |
| 154 |
| 155 values = filter.FilterForValue(1, &filter_offset, &filter_length); |
| 156 ASSERT_TRUE(values != NULL); |
| 157 ASSERT_EQ(23, filter_offset); // 22 plus 1 leading zero |
| 158 ASSERT_EQ(4, filter_length); // 5 - 1 leading zero |
| 159 |
| 160 // Zeroes on the right are ignored |
| 161 static const float factors3[] = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f }; |
| 162 filter.AddFilter(33, factors3, arraysize(factors3)); |
| 163 ASSERT_EQ(5, filter.max_filter()); |
| 164 ASSERT_EQ(3, filter.num_values()); |
| 165 |
| 166 values = filter.FilterForValue(2, &filter_offset, &filter_length); |
| 167 ASSERT_TRUE(values != NULL); |
| 168 ASSERT_EQ(33, filter_offset); // 33, same as input due to no leading zero |
| 169 ASSERT_EQ(5, filter_length); // 7 - 2 trailing zeroes |
| 170 |
| 171 // Zeroes in leading & trailing positions |
| 172 static const float factors4[] = { 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f }; |
| 173 filter.AddFilter(44, factors4, arraysize(factors4)); |
| 174 ASSERT_EQ(5, filter.max_filter()); // No change from existing value. |
| 175 ASSERT_EQ(4, filter.num_values()); |
| 176 |
| 177 values = filter.FilterForValue(3, &filter_offset, &filter_length); |
| 178 ASSERT_TRUE(values != NULL); |
| 179 ASSERT_EQ(46, filter_offset); // 44 plus 2 leading zeroes |
| 180 ASSERT_EQ(3, filter_length); // 7 - (2 leading + 2 trailing) zeroes |
| 181 |
| 182 // Zeroes surrounded by non-zero values are ignored |
| 183 static const float factors5[] = { 0.0f, 0.0f, |
| 184 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, |
| 185 0.0f }; |
| 186 filter.AddFilter(55, factors5, arraysize(factors5)); |
| 187 ASSERT_EQ(6, filter.max_filter()); |
| 188 ASSERT_EQ(5, filter.num_values()); |
| 189 |
| 190 values = filter.FilterForValue(4, &filter_offset, &filter_length); |
| 191 ASSERT_TRUE(values != NULL); |
| 192 ASSERT_EQ(57, filter_offset); // 55 plus 2 leading zeroes |
| 193 ASSERT_EQ(6, filter_length); // 9 - (2 leading + 1 trailing) zeroes |
| 194 |
| 195 // All-zero filters after the first one also work |
| 196 static const float factors6[] = { 0.0f }; |
| 197 filter.AddFilter(66, factors6, arraysize(factors6)); |
| 198 ASSERT_EQ(6, filter.max_filter()); |
| 199 ASSERT_EQ(6, filter.num_values()); |
| 200 |
| 201 values = filter.FilterForValue(5, &filter_offset, &filter_length); |
| 202 ASSERT_TRUE(values == NULL); // filter_length == 0 => values is NULL |
| 203 ASSERT_EQ(66, filter_offset); // value passed in |
| 204 ASSERT_EQ(0, filter_length); |
| 205 } |
| 206 |
126 } // namespace skia | 207 } // namespace skia |
127 | |
OLD | NEW |