OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "ui/gfx/content_analysis.h" | |
6 | |
7 #include <algorithm> | |
8 #include <cmath> | |
9 #include <cstdlib> | |
10 #include <limits> | |
11 #include <numeric> | |
12 #include <vector> | |
13 | |
14 #include "base/memory/scoped_ptr.h" | |
15 #include "testing/gtest/include/gtest/gtest.h" | |
16 #include "third_party/skia/include/core/SkBitmap.h" | |
17 #include "third_party/skia/include/core/SkColor.h" | |
18 #include "ui/gfx/canvas.h" | |
19 #include "ui/gfx/color_analysis.h" | |
20 #include "ui/gfx/color_utils.h" | |
21 #include "ui/gfx/image/image.h" | |
22 #include "ui/gfx/rect.h" | |
23 #include "ui/gfx/size.h" | |
24 | |
25 namespace { | |
26 | |
27 #ifndef M_PI | |
28 #define M_PI 3.14159265358979323846 | |
29 #endif | |
30 | |
31 unsigned long ImagePixelSum(const SkBitmap& bitmap, const gfx::Rect& rect) { | |
32 // Get the sum of pixel values in the rectangle. Applicable only to | |
33 // monochrome bitmaps. | |
34 DCHECK_EQ(SkBitmap::kA8_Config, bitmap.config()); | |
35 unsigned long total = 0; | |
36 for (int r = rect.y(); r < rect.bottom(); ++r) { | |
37 const uint8* row_data = static_cast<const uint8*>( | |
38 bitmap.getPixels()) + r * bitmap.rowBytes(); | |
39 for (int c = rect.x(); c < rect.right(); ++c) | |
40 total += row_data[c]; | |
41 } | |
42 | |
43 return total; | |
44 } | |
45 | |
46 bool CompareImageFragments(const SkBitmap& bitmap_left, | |
47 const SkBitmap& bitmap_right, | |
48 const gfx::Size& comparison_area, | |
49 const gfx::Point& origin_left, | |
50 const gfx::Point& origin_right) { | |
51 SkAutoLockPixels left_lock(bitmap_left); | |
52 SkAutoLockPixels right_lock(bitmap_right); | |
53 for (int r = 0; r < comparison_area.height(); ++r) { | |
54 for (int c = 0; c < comparison_area.width(); ++c) { | |
55 SkColor clr_left = bitmap_left.getColor(origin_left.x() + c, | |
Alexei Svitkine (slow)
2013/04/15 13:15:36
Nit: Don't use abbrevs.
motek.
2013/04/15 14:47:47
Done.
| |
56 origin_left.y() + r); | |
57 SkColor clr_right = bitmap_right.getColor(origin_right.x() + c, | |
58 origin_right.y() + r); | |
59 if (clr_left != clr_right) | |
60 return false; | |
61 } | |
62 } | |
63 | |
64 return true; | |
65 } | |
66 | |
67 } // namespace | |
68 | |
69 class ContentAnalysisTest : public testing::Test { | |
70 }; | |
71 | |
72 TEST_F(ContentAnalysisTest, ApplyGradientMagnitudeOnImpulse) { | |
73 gfx::Canvas canvas(gfx::Size(800, 600), ui::SCALE_FACTOR_100P, true); | |
74 | |
75 // The image consists of vertical non-overlapping stripes 100 pixels wide. | |
76 canvas.FillRect(gfx::Rect(0, 0, 800, 600), SkColorSetARGB(0, 10, 10, 10)); | |
77 canvas.FillRect(gfx::Rect(400, 300, 1, 1), SkColorSetRGB(255, 255, 255)); | |
78 | |
79 SkBitmap source = | |
80 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false); | |
81 | |
82 SkBitmap reduced_color; | |
83 reduced_color.setConfig( | |
84 SkBitmap::kA8_Config, source.width(), source.height()); | |
85 reduced_color.allocPixels(); | |
86 | |
87 gfx::Vector3dF transform(0.299f, 0.587f, 0.114f); | |
88 EXPECT_TRUE(color_utils::ApplyColorReduction( | |
89 source, transform, true, &reduced_color)); | |
90 | |
91 float sigma = 2.5f; | |
92 color_utils::ApplyGaussianGradientMagnitudeFilter(&reduced_color, sigma); | |
93 | |
94 // Expect everything to be within 8 * sigma. | |
95 int tail_length = static_cast<int>(8.0f * sigma + 0.5f); | |
96 gfx::Rect echo_rect(399 - tail_length, 299 - tail_length, | |
97 2 * tail_length + 1, 2 * tail_length + 1); | |
98 unsigned long data_sum = ImagePixelSum(reduced_color, echo_rect); | |
99 unsigned long all_sum = ImagePixelSum(reduced_color, gfx::Rect(800, 600)); | |
100 EXPECT_GT(data_sum, 0U); | |
101 EXPECT_EQ(data_sum, all_sum); | |
102 | |
103 sigma = 5.0f; | |
104 color_utils::ApplyGaussianGradientMagnitudeFilter(&reduced_color, sigma); | |
105 | |
106 // Expect everything to be within 8 * sigma. | |
107 tail_length = static_cast<int>(8.0f * sigma + 0.5f); | |
108 echo_rect = gfx::Rect(399 - tail_length, 299 - tail_length, | |
109 2 * tail_length + 1, 2 * tail_length + 1); | |
110 data_sum = ImagePixelSum(reduced_color, echo_rect); | |
111 all_sum = ImagePixelSum(reduced_color, gfx::Rect(800, 600)); | |
112 EXPECT_GT(data_sum, 0U); | |
113 EXPECT_EQ(data_sum, all_sum); | |
114 } | |
115 | |
116 TEST_F(ContentAnalysisTest, ApplyGradientMagnitudeOnFrame) { | |
117 gfx::Canvas canvas(gfx::Size(800, 600), ui::SCALE_FACTOR_100P, true); | |
118 | |
119 // The image consists of a single white block in the centre. | |
120 gfx::Rect draw_rect(300, 200, 200, 200); | |
121 canvas.FillRect(gfx::Rect(0, 0, 800, 600), SkColorSetARGB(0, 0, 0, 0)); | |
122 canvas.DrawRect(draw_rect, SkColorSetRGB(255, 255, 255)); | |
123 | |
124 SkBitmap source = | |
125 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false); | |
126 | |
127 SkBitmap reduced_color; | |
128 reduced_color.setConfig( | |
129 SkBitmap::kA8_Config, source.width(), source.height()); | |
130 reduced_color.allocPixels(); | |
131 | |
132 gfx::Vector3dF transform(0.299f, 0.587f, 0.114f); | |
133 EXPECT_TRUE(color_utils::ApplyColorReduction( | |
134 source, transform, true, &reduced_color)); | |
135 | |
136 float sigma = 2.5f; | |
137 color_utils::ApplyGaussianGradientMagnitudeFilter(&reduced_color, sigma); | |
138 | |
139 int tail_length = static_cast<int>(8.0f * sigma + 0.5f); | |
140 gfx::Rect outer_rect(draw_rect.x() - tail_length, | |
141 draw_rect.y() - tail_length, | |
142 draw_rect.width() + 2 * tail_length, | |
143 draw_rect.height() + 2 * tail_length); | |
144 gfx::Rect inner_rect(draw_rect.x() + tail_length, | |
145 draw_rect.y() + tail_length, | |
146 draw_rect.width() - 2 * tail_length, | |
147 draw_rect.height() - 2 * tail_length); | |
148 unsigned long data_sum = ImagePixelSum(reduced_color, outer_rect); | |
149 unsigned long all_sum = ImagePixelSum(reduced_color, gfx::Rect(800, 600)); | |
150 EXPECT_GT(data_sum, 0U); | |
151 EXPECT_EQ(data_sum, all_sum); | |
152 EXPECT_EQ(ImagePixelSum(reduced_color, inner_rect), 0U); | |
153 } | |
154 | |
155 TEST_F(ContentAnalysisTest, ExtractImageProfileInformation) { | |
156 gfx::Canvas canvas(gfx::Size(800, 600), ui::SCALE_FACTOR_100P, true); | |
157 | |
158 // The image consists of a white frame drawn in the centre. | |
159 gfx::Rect draw_rect(100, 100, 200, 100); | |
160 gfx::Rect image_rect(0, 0, 800, 600); | |
161 canvas.FillRect(image_rect, SkColorSetARGB(0, 0, 0, 0)); | |
162 canvas.DrawRect(draw_rect, SkColorSetRGB(255, 255, 255)); | |
163 | |
164 SkBitmap source = | |
165 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false); | |
166 SkBitmap reduced_color; | |
167 reduced_color.setConfig( | |
168 SkBitmap::kA8_Config, source.width(), source.height()); | |
169 reduced_color.allocPixels(); | |
170 | |
171 gfx::Vector3dF transform(1, 0, 0); | |
172 EXPECT_TRUE(color_utils::ApplyColorReduction( | |
173 source, transform, true, &reduced_color)); | |
174 std::vector<float> column_profile; | |
175 std::vector<float> row_profile; | |
176 color_utils::ExtractImageProfileInformation(reduced_color, | |
177 image_rect, | |
178 gfx::Size(), | |
179 false, | |
180 &row_profile, | |
181 &column_profile); | |
182 EXPECT_EQ(0, std::accumulate(column_profile.begin(), | |
183 column_profile.begin() + draw_rect.x() - 1, | |
184 0)); | |
185 EXPECT_EQ(column_profile[draw_rect.x()], 255U * (draw_rect.height() + 1)); | |
186 EXPECT_EQ(2 * 255 * (draw_rect.width() - 2), | |
187 std::accumulate(column_profile.begin() + draw_rect.x() + 1, | |
188 column_profile.begin() + draw_rect.right() - 1, | |
189 0)); | |
190 | |
191 EXPECT_EQ(0, std::accumulate(row_profile.begin(), | |
192 row_profile.begin() + draw_rect.y() - 1, | |
193 0)); | |
194 EXPECT_EQ(row_profile[draw_rect.y()], 255U * (draw_rect.width() + 1)); | |
195 EXPECT_EQ(2 * 255 * (draw_rect.height() - 2), | |
196 std::accumulate(row_profile.begin() + draw_rect.y() + 1, | |
197 row_profile.begin() + draw_rect.bottom() - 1, | |
198 0)); | |
199 | |
200 gfx::Rect test_rect(150, 80, 400, 100); | |
201 color_utils::ExtractImageProfileInformation(reduced_color, | |
202 test_rect, | |
203 gfx::Size(), | |
204 false, | |
205 &row_profile, | |
206 &column_profile); | |
207 | |
208 // Some overlap with the drawn rectagle. If you work it out on a piece of | |
209 // paper, sums should be as follows. | |
210 EXPECT_EQ(255 * (test_rect.bottom() - draw_rect.y()) + | |
211 255 * (draw_rect.right() - test_rect.x()), | |
212 std::accumulate(row_profile.begin(), row_profile.end(), 0)); | |
213 EXPECT_EQ(255 * (test_rect.bottom() - draw_rect.y()) + | |
214 255 * (draw_rect.right() - test_rect.x()), | |
215 std::accumulate(column_profile.begin(), column_profile.end(), 0)); | |
216 } | |
217 | |
218 TEST_F(ContentAnalysisTest, ExtractImageProfileInformationWithClosing) { | |
219 gfx::Canvas canvas(gfx::Size(800, 600), ui::SCALE_FACTOR_100P, true); | |
220 | |
221 // The image consists of a two white frames drawn side by side, with a | |
222 // single-pixel vertical gap in between. | |
223 gfx::Rect image_rect(0, 0, 800, 600); | |
224 canvas.FillRect(image_rect, SkColorSetARGB(0, 0, 0, 0)); | |
225 canvas.DrawRect(gfx::Rect(300, 250, 99, 100), SkColorSetRGB(255, 255, 255)); | |
226 canvas.DrawRect(gfx::Rect(401, 250, 99, 100), SkColorSetRGB(255, 255, 255)); | |
227 | |
228 SkBitmap source = | |
229 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false); | |
230 SkBitmap reduced_color; | |
231 reduced_color.setConfig( | |
232 SkBitmap::kA8_Config, source.width(), source.height()); | |
233 reduced_color.allocPixels(); | |
234 | |
235 gfx::Vector3dF transform(1, 0, 0); | |
236 EXPECT_TRUE(color_utils::ApplyColorReduction( | |
237 source, transform, true, &reduced_color)); | |
238 std::vector<float> column_profile; | |
239 std::vector<float> row_profile; | |
240 | |
241 color_utils::ExtractImageProfileInformation(reduced_color, | |
242 image_rect, | |
243 gfx::Size(), | |
244 true, | |
245 &row_profile, | |
246 &column_profile); | |
247 // Column profiles should have two spikes in the middle, with a single | |
248 // 0-valued value between them. | |
249 EXPECT_GT(column_profile[398], 0.0f); | |
250 EXPECT_GT(column_profile[399], column_profile[398]); | |
251 EXPECT_GT(column_profile[402], 0.0f); | |
252 EXPECT_GT(column_profile[401], column_profile[402]); | |
253 EXPECT_EQ(column_profile[401], column_profile[399]); | |
254 EXPECT_EQ(column_profile[402], column_profile[398]); | |
255 EXPECT_EQ(column_profile[400], 0.0f); | |
256 EXPECT_EQ(column_profile[299], 0.0f); | |
257 EXPECT_EQ(column_profile[502], 0.0f); | |
258 | |
259 // Now the same with closing applied. The space in the middle will be closed. | |
260 color_utils::ExtractImageProfileInformation(reduced_color, | |
261 image_rect, | |
262 gfx::Size(200, 100), | |
263 true, | |
264 &row_profile, | |
265 &column_profile); | |
266 EXPECT_GT(column_profile[398], 0); | |
267 EXPECT_GT(column_profile[400], 0); | |
268 EXPECT_GT(column_profile[402], 0); | |
269 EXPECT_EQ(column_profile[299], 0); | |
270 EXPECT_EQ(column_profile[502], 0); | |
271 EXPECT_EQ(column_profile[399], column_profile[401]); | |
272 EXPECT_EQ(column_profile[398], column_profile[402]); | |
273 } | |
274 | |
275 TEST_F(ContentAnalysisTest, AutoSegmentPeaks) { | |
276 std::vector<float> profile_info; | |
277 | |
278 EXPECT_EQ(color_utils::AutoSegmentPeaks(profile_info), | |
279 std::numeric_limits<float>::max()); | |
280 profile_info.resize(1000, 1.0f); | |
281 EXPECT_EQ(color_utils::AutoSegmentPeaks(profile_info), 1.0f); | |
282 std::srand(42); | |
283 std::generate(profile_info.begin(), profile_info.end(), std::rand); | |
284 float threshold = color_utils::AutoSegmentPeaks(profile_info); | |
285 EXPECT_GT(threshold, 0); // Not much to expect. | |
286 | |
287 // There should be roughly 50% above and below the threshold. | |
288 // Random is not really random thanks to srand, so we can sort-of compare. | |
289 int above_count = std::count_if( | |
290 profile_info.begin(), | |
291 profile_info.end(), | |
292 std::bind2nd(std::greater<float>(), threshold)); | |
293 EXPECT_GT(above_count, 450); // Not much to expect. | |
294 EXPECT_LT(above_count, 550); | |
295 | |
296 for (unsigned i = 0; i < profile_info.size(); ++i) { | |
297 float y = std::sin(M_PI * i / 250.0f); | |
298 profile_info[i] = y > 0 ? y : 0; | |
299 } | |
300 threshold = color_utils::AutoSegmentPeaks(profile_info); | |
301 | |
302 above_count = std::count_if( | |
303 profile_info.begin(), | |
304 profile_info.end(), | |
305 std::bind2nd(std::greater<float>(), threshold)); | |
306 EXPECT_LT(above_count, 500); // Negative y expected to fall below threshold. | |
307 | |
308 // Expect two peaks around between 0 and 250 and 500 and 750. | |
309 std::vector<bool> thresholded_values(profile_info.size(), false); | |
310 std::transform(profile_info.begin(), | |
311 profile_info.end(), | |
312 thresholded_values.begin(), | |
313 std::bind2nd(std::greater<float>(), threshold)); | |
314 EXPECT_TRUE(thresholded_values[125]); | |
315 EXPECT_TRUE(thresholded_values[625]); | |
316 int transitions = 0; | |
317 for (unsigned i = 1; i < thresholded_values.size(); ++i) { | |
318 if (thresholded_values[i] != thresholded_values[i-1]) | |
319 transitions++; | |
320 } | |
321 EXPECT_EQ(transitions, 4); // We have two contiguous peaks. Good going! | |
322 } | |
323 | |
324 TEST_F(ContentAnalysisTest, ComputeDecimatedImage) { | |
325 gfx::Size image_size(1600, 1200); | |
326 gfx::Canvas canvas(image_size, ui::SCALE_FACTOR_100P, true); | |
327 | |
328 // Make some content we will later want to keep. | |
329 canvas.FillRect(gfx::Rect(100, 200, 100, 100), SkColorSetARGB(0, 125, 0, 0)); | |
330 canvas.FillRect(gfx::Rect(300, 200, 100, 100), SkColorSetARGB(0, 0, 200, 0)); | |
331 canvas.FillRect(gfx::Rect(500, 200, 100, 100), SkColorSetARGB(0, 0, 0, 225)); | |
332 canvas.FillRect(gfx::Rect(100, 400, 600, 100), | |
333 SkColorSetARGB(0, 125, 200, 225)); | |
334 | |
335 std::vector<bool> rows(image_size.height(), false); | |
336 std::fill_n(rows.begin() + 200, 100, true); | |
337 std::fill_n(rows.begin() + 400, 100, true); | |
338 | |
339 std::vector<bool> columns(image_size.width(), false); | |
340 std::fill_n(columns.begin() + 100, 100, true); | |
341 std::fill_n(columns.begin() + 300, 100, true); | |
342 std::fill_n(columns.begin() + 500, 100, true); | |
343 | |
344 SkBitmap source = | |
345 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false); | |
346 SkBitmap result = color_utils::ComputeDecimatedImage(source, rows, columns); | |
347 EXPECT_FALSE(result.empty()); | |
348 EXPECT_EQ(300, result.width()); | |
349 EXPECT_EQ(200, result.height()); | |
350 | |
351 // The call should have removed all empty spaces. | |
352 ASSERT_TRUE(CompareImageFragments(source, | |
353 result, | |
354 gfx::Size(100, 100), | |
355 gfx::Point(100, 200), | |
356 gfx::Point(0, 0))); | |
357 ASSERT_TRUE(CompareImageFragments(source, | |
358 result, | |
359 gfx::Size(100, 100), | |
360 gfx::Point(300, 200), | |
361 gfx::Point(100, 0))); | |
362 ASSERT_TRUE(CompareImageFragments(source, | |
363 result, | |
364 gfx::Size(100, 100), | |
365 gfx::Point(500, 200), | |
366 gfx::Point(200, 0))); | |
367 ASSERT_TRUE(CompareImageFragments(source, | |
368 result, | |
369 gfx::Size(100, 100), | |
370 gfx::Point(100, 400), | |
371 gfx::Point(0, 0))); | |
372 } | |
373 | |
374 TEST_F(ContentAnalysisTest, CreateRetargettedThumbnailImage) { | |
375 gfx::Size image_size(1200, 1300); | |
376 gfx::Canvas canvas(image_size, ui::SCALE_FACTOR_100P, true); | |
377 | |
378 // The following will create a 'fake image' consisting of color blocks placed | |
379 // on a neutral background. The entire layout is supposed to mimic a | |
380 // screenshot of a web page. | |
381 // The tested function is supposed to locate the interesing areas in the | |
382 // middle. | |
383 const int margin_horizontal = 60; | |
384 const int margin_vertical = 20; | |
385 canvas.FillRect(gfx::Rect(image_size), SkColorSetRGB(200, 210, 210)); | |
386 const gfx::Rect header_rect(margin_horizontal, | |
387 margin_vertical, | |
388 image_size.width() - 2 * margin_horizontal, | |
389 100); | |
390 const gfx::Rect footer_rect(margin_horizontal, | |
391 image_size.height() - margin_vertical - 100, | |
392 image_size.width() - 2 * margin_horizontal, | |
393 100); | |
394 const gfx::Rect body_rect(margin_horizontal, | |
395 header_rect.bottom() + margin_vertical, | |
396 image_size.width() - 2 * margin_horizontal, | |
397 footer_rect.y() - header_rect.bottom() - | |
398 2 * margin_vertical); | |
399 canvas.FillRect(header_rect, SkColorSetRGB(200, 40, 10)); | |
400 canvas.FillRect(footer_rect, SkColorSetRGB(10, 40, 180)); | |
401 canvas.FillRect(body_rect, SkColorSetRGB(150, 180, 40)); | |
402 | |
403 // 'Fine print' at the bottom. | |
404 const int fine_print = 8; | |
405 const SkColor print_color = SkColorSetRGB(45, 30, 30); | |
406 for (int y = footer_rect.y() + fine_print; | |
407 y < footer_rect.bottom() - fine_print; | |
408 y += 2 * fine_print) { | |
409 for (int x = footer_rect.x() + fine_print; | |
410 x < footer_rect.right() - fine_print; | |
411 x += 2 * fine_print) { | |
412 canvas.DrawRect(gfx::Rect(x, y, fine_print, fine_print), print_color); | |
413 } | |
414 } | |
415 | |
416 // Blocky content at the top. | |
417 const int block_size = header_rect.height() - margin_vertical; | |
418 for (int x = header_rect.x() + margin_horizontal; | |
419 x < header_rect.right() - block_size; | |
420 x += block_size + margin_horizontal) { | |
421 const int half_block = block_size / 2 - 5; | |
422 const SkColor block_color = SkColorSetRGB(255, 255, 255); | |
423 const int y = header_rect.y() + margin_vertical / 2; | |
424 int second_col = x + half_block + 10; | |
425 int second_row = y + half_block + 10; | |
426 canvas.FillRect(gfx::Rect(x, y, half_block, block_size), block_color); | |
427 canvas.FillRect(gfx::Rect(second_col, y, half_block, half_block), | |
428 block_color); | |
429 canvas.FillRect(gfx::Rect(second_col, second_row, half_block, half_block), | |
430 block_color); | |
431 } | |
432 | |
433 // Now the main body. Mostly text with some 'pictures'. | |
434 for (int y = body_rect.y() + fine_print; | |
435 y < body_rect.bottom() - fine_print; | |
436 y += 2 * fine_print) { | |
437 for (int x = body_rect.x() + fine_print; | |
438 x < body_rect.right() - fine_print; | |
439 x += 2 * fine_print) { | |
440 canvas.DrawRect(gfx::Rect(x, y, fine_print, fine_print), print_color); | |
441 } | |
442 } | |
443 | |
444 for (int line = 0; line < 3; ++line) { | |
445 int alignment = line % 2; | |
446 const int y = body_rect.y() + | |
447 body_rect.height() / 3 * line + margin_vertical; | |
448 const int x = body_rect.x() + | |
449 alignment * body_rect.width() / 2 + margin_vertical; | |
450 gfx::Rect pict_rect(x, y, | |
451 body_rect.width() / 2 - 2 * margin_vertical, | |
452 body_rect.height() / 3 - 2 * margin_vertical); | |
453 canvas.FillRect(pict_rect, SkColorSetRGB(255, 255, 255)); | |
454 canvas.DrawRect(pict_rect, SkColorSetRGB(0, 0, 0)); | |
455 } | |
456 | |
457 SkBitmap source = | |
458 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false); | |
459 | |
460 SkBitmap result = color_utils::CreateRetargettedThumbnailImage( | |
461 source, gfx::Size(424, 264), 2.5); | |
462 EXPECT_FALSE(result.empty()); | |
463 | |
464 // Given the nature of computation We can't really assert much here about the | |
465 // image itself. We know it should have been computed, should be smaller than | |
466 // the original and it must not be zero. | |
467 EXPECT_LT(result.width(), image_size.width()); | |
468 EXPECT_LT(result.height(), image_size.height()); | |
469 | |
470 int histogram[256]; | |
471 color_utils::BuildLumaHistogram(result, histogram); | |
472 int non_zero_color_count = std::count_if( | |
473 histogram, histogram + 256, std::bind2nd(std::greater<int>(), 0)); | |
474 EXPECT_GT(non_zero_color_count, 4); | |
475 } | |
OLD | NEW |