Give names to all utility processes.
[chromium-blink-merge.git] / chrome / browser / thumbnails / content_analysis_unittest.cc
blobb1663cf28c324ce54abe655547c55f98d7d160fc
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.
5 #include "chrome/browser/thumbnails/content_analysis.h"
7 #include <algorithm>
8 #include <cmath>
9 #include <cstdlib>
10 #include <functional>
11 #include <limits>
12 #include <numeric>
13 #include <vector>
15 #include "base/memory/scoped_ptr.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 #include "third_party/skia/include/core/SkBitmap.h"
18 #include "third_party/skia/include/core/SkColor.h"
19 #include "ui/gfx/canvas.h"
20 #include "ui/gfx/color_analysis.h"
21 #include "ui/gfx/color_utils.h"
22 #include "ui/gfx/geometry/rect.h"
23 #include "ui/gfx/geometry/size.h"
24 #include "ui/gfx/image/image.h"
26 namespace {
28 #ifndef M_PI
29 #define M_PI 3.14159265358979323846
30 #endif
32 unsigned long ImagePixelSum(const SkBitmap& bitmap, const gfx::Rect& rect) {
33 // Get the sum of pixel values in the rectangle. Applicable only to
34 // monochrome bitmaps.
35 DCHECK_EQ(kAlpha_8_SkColorType, bitmap.colorType());
36 unsigned long total = 0;
37 for (int r = rect.y(); r < rect.bottom(); ++r) {
38 const uint8* row_data = static_cast<const uint8*>(
39 bitmap.getPixels()) + r * bitmap.rowBytes();
40 for (int c = rect.x(); c < rect.right(); ++c)
41 total += row_data[c];
44 return total;
47 bool CompareImageFragments(const SkBitmap& bitmap_left,
48 const SkBitmap& bitmap_right,
49 const gfx::Size& comparison_area,
50 const gfx::Point& origin_left,
51 const gfx::Point& origin_right) {
52 SkAutoLockPixels left_lock(bitmap_left);
53 SkAutoLockPixels right_lock(bitmap_right);
54 for (int r = 0; r < comparison_area.height(); ++r) {
55 for (int c = 0; c < comparison_area.width(); ++c) {
56 SkColor color_left = bitmap_left.getColor(origin_left.x() + c,
57 origin_left.y() + r);
58 SkColor color_right = bitmap_right.getColor(origin_right.x() + c,
59 origin_right.y() + r);
60 if (color_left != color_right)
61 return false;
65 return true;
68 float AspectDifference(const gfx::Size& reference, const gfx::Size& candidate) {
69 return std::abs(static_cast<float>(candidate.width()) / candidate.height() -
70 static_cast<float>(reference.width()) / reference.height());
73 } // namespace
75 namespace thumbnailing_utils {
77 class ThumbnailContentAnalysisTest : public testing::Test {
80 TEST_F(ThumbnailContentAnalysisTest, ApplyGradientMagnitudeOnImpulse) {
81 gfx::Canvas canvas(gfx::Size(800, 600), 1.0f, true);
83 // The image consists of a point spike on uniform (non-zero) background.
84 canvas.FillRect(gfx::Rect(0, 0, 800, 600), SkColorSetRGB(10, 10, 10));
85 canvas.FillRect(gfx::Rect(400, 300, 1, 1), SkColorSetRGB(255, 255, 255));
87 SkBitmap source =
88 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
90 SkBitmap reduced_color;
91 reduced_color.allocPixels(SkImageInfo::MakeA8(source.width(),
92 source.height()));
94 gfx::Vector3dF transform(0.299f, 0.587f, 0.114f);
95 EXPECT_TRUE(color_utils::ApplyColorReduction(
96 source, transform, true, &reduced_color));
98 float sigma = 2.5f;
99 ApplyGaussianGradientMagnitudeFilter(&reduced_color, sigma);
101 // Expect everything to be within 8 * sigma.
102 int tail_length = static_cast<int>(8.0f * sigma + 0.5f);
103 gfx::Rect echo_rect(399 - tail_length, 299 - tail_length,
104 2 * tail_length + 1, 2 * tail_length + 1);
105 unsigned long data_sum = ImagePixelSum(reduced_color, echo_rect);
106 unsigned long all_sum = ImagePixelSum(reduced_color, gfx::Rect(800, 600));
107 EXPECT_GT(data_sum, 0U);
108 EXPECT_EQ(data_sum, all_sum);
110 sigma = 5.0f;
111 ApplyGaussianGradientMagnitudeFilter(&reduced_color, sigma);
113 // Expect everything to be within 8 * sigma.
114 tail_length = static_cast<int>(8.0f * sigma + 0.5f);
115 echo_rect = gfx::Rect(399 - tail_length, 299 - tail_length,
116 2 * tail_length + 1, 2 * tail_length + 1);
117 data_sum = ImagePixelSum(reduced_color, echo_rect);
118 all_sum = ImagePixelSum(reduced_color, gfx::Rect(800, 600));
119 EXPECT_GT(data_sum, 0U);
120 EXPECT_EQ(data_sum, all_sum);
123 TEST_F(ThumbnailContentAnalysisTest, ApplyGradientMagnitudeOnFrame) {
124 gfx::Canvas canvas(gfx::Size(800, 600), 1.0f, true);
126 // The image consists of a single white block in the centre.
127 gfx::Rect draw_rect(300, 200, 200, 200);
128 canvas.FillRect(gfx::Rect(0, 0, 800, 600), SkColorSetRGB(0, 0, 0));
129 canvas.DrawRect(draw_rect, SkColorSetRGB(255, 255, 255));
131 SkBitmap source =
132 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
134 SkBitmap reduced_color;
135 reduced_color.allocPixels(SkImageInfo::MakeA8(source.width(),
136 source.height()));
138 gfx::Vector3dF transform(0.299f, 0.587f, 0.114f);
139 EXPECT_TRUE(color_utils::ApplyColorReduction(
140 source, transform, true, &reduced_color));
142 float sigma = 2.5f;
143 ApplyGaussianGradientMagnitudeFilter(&reduced_color, sigma);
145 int tail_length = static_cast<int>(8.0f * sigma + 0.5f);
146 gfx::Rect outer_rect(draw_rect.x() - tail_length,
147 draw_rect.y() - tail_length,
148 draw_rect.width() + 2 * tail_length,
149 draw_rect.height() + 2 * tail_length);
150 gfx::Rect inner_rect(draw_rect.x() + tail_length,
151 draw_rect.y() + tail_length,
152 draw_rect.width() - 2 * tail_length,
153 draw_rect.height() - 2 * tail_length);
154 unsigned long data_sum = ImagePixelSum(reduced_color, outer_rect);
155 unsigned long all_sum = ImagePixelSum(reduced_color, gfx::Rect(800, 600));
156 EXPECT_GT(data_sum, 0U);
157 EXPECT_EQ(data_sum, all_sum);
158 EXPECT_EQ(ImagePixelSum(reduced_color, inner_rect), 0U);
161 TEST_F(ThumbnailContentAnalysisTest, ExtractImageProfileInformation) {
162 gfx::Canvas canvas(gfx::Size(800, 600), 1.0f, true);
164 // The image consists of a white frame drawn in the centre.
165 gfx::Rect draw_rect(100, 100, 200, 100);
166 gfx::Rect image_rect(0, 0, 800, 600);
167 canvas.FillRect(image_rect, SkColorSetRGB(0, 0, 0));
168 canvas.DrawRect(draw_rect, SkColorSetRGB(255, 255, 255));
170 SkBitmap source =
171 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
172 SkBitmap reduced_color;
173 reduced_color.allocPixels(SkImageInfo::MakeA8(source.width(),
174 source.height()));
176 gfx::Vector3dF transform(1, 0, 0);
177 EXPECT_TRUE(color_utils::ApplyColorReduction(
178 source, transform, true, &reduced_color));
179 std::vector<float> column_profile;
180 std::vector<float> row_profile;
181 ExtractImageProfileInformation(reduced_color,
182 image_rect,
183 gfx::Size(),
184 false,
185 &row_profile,
186 &column_profile);
187 EXPECT_EQ(0, std::accumulate(column_profile.begin(),
188 column_profile.begin() + draw_rect.x() - 1,
189 0));
190 EXPECT_EQ(column_profile[draw_rect.x()], 255U * (draw_rect.height() + 1));
191 EXPECT_EQ(2 * 255 * (draw_rect.width() - 2),
192 std::accumulate(column_profile.begin() + draw_rect.x() + 1,
193 column_profile.begin() + draw_rect.right() - 1,
194 0));
196 EXPECT_EQ(0, std::accumulate(row_profile.begin(),
197 row_profile.begin() + draw_rect.y() - 1,
198 0));
199 EXPECT_EQ(row_profile[draw_rect.y()], 255U * (draw_rect.width() + 1));
200 EXPECT_EQ(2 * 255 * (draw_rect.height() - 2),
201 std::accumulate(row_profile.begin() + draw_rect.y() + 1,
202 row_profile.begin() + draw_rect.bottom() - 1,
203 0));
205 gfx::Rect test_rect(150, 80, 400, 100);
206 ExtractImageProfileInformation(reduced_color,
207 test_rect,
208 gfx::Size(),
209 false,
210 &row_profile,
211 &column_profile);
213 // Some overlap with the drawn rectagle. If you work it out on a piece of
214 // paper, sums should be as follows.
215 EXPECT_EQ(255 * (test_rect.bottom() - draw_rect.y()) +
216 255 * (draw_rect.right() - test_rect.x()),
217 std::accumulate(row_profile.begin(), row_profile.end(), 0));
218 EXPECT_EQ(255 * (test_rect.bottom() - draw_rect.y()) +
219 255 * (draw_rect.right() - test_rect.x()),
220 std::accumulate(column_profile.begin(), column_profile.end(), 0));
223 TEST_F(ThumbnailContentAnalysisTest,
224 ExtractImageProfileInformationWithClosing) {
225 gfx::Canvas canvas(gfx::Size(800, 600), 1.0f, true);
227 // The image consists of a two white frames drawn side by side, with a
228 // single-pixel vertical gap in between.
229 gfx::Rect image_rect(0, 0, 800, 600);
230 canvas.FillRect(image_rect, SkColorSetRGB(0, 0, 0));
231 canvas.DrawRect(gfx::Rect(300, 250, 99, 100), SkColorSetRGB(255, 255, 255));
232 canvas.DrawRect(gfx::Rect(401, 250, 99, 100), SkColorSetRGB(255, 255, 255));
234 SkBitmap source =
235 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
236 SkBitmap reduced_color;
237 reduced_color.allocPixels(SkImageInfo::MakeA8(source.width(),
238 source.height()));
240 gfx::Vector3dF transform(1, 0, 0);
241 EXPECT_TRUE(color_utils::ApplyColorReduction(
242 source, transform, true, &reduced_color));
243 std::vector<float> column_profile;
244 std::vector<float> row_profile;
246 ExtractImageProfileInformation(reduced_color,
247 image_rect,
248 gfx::Size(),
249 true,
250 &row_profile,
251 &column_profile);
252 // Column profiles should have two spikes in the middle, with a single
253 // 0-valued value between them.
254 EXPECT_GT(column_profile[398], 0.0f);
255 EXPECT_GT(column_profile[399], column_profile[398]);
256 EXPECT_GT(column_profile[402], 0.0f);
257 EXPECT_GT(column_profile[401], column_profile[402]);
258 EXPECT_EQ(column_profile[401], column_profile[399]);
259 EXPECT_EQ(column_profile[402], column_profile[398]);
260 EXPECT_EQ(column_profile[400], 0.0f);
261 EXPECT_EQ(column_profile[299], 0.0f);
262 EXPECT_EQ(column_profile[502], 0.0f);
264 // Now the same with closing applied. The space in the middle will be closed.
265 ExtractImageProfileInformation(reduced_color,
266 image_rect,
267 gfx::Size(200, 100),
268 true,
269 &row_profile,
270 &column_profile);
271 EXPECT_GT(column_profile[398], 0);
272 EXPECT_GT(column_profile[400], 0);
273 EXPECT_GT(column_profile[402], 0);
274 EXPECT_EQ(column_profile[299], 0);
275 EXPECT_EQ(column_profile[502], 0);
276 EXPECT_EQ(column_profile[399], column_profile[401]);
277 EXPECT_EQ(column_profile[398], column_profile[402]);
280 TEST_F(ThumbnailContentAnalysisTest, AdjustClippingSizeToAspectRatio) {
281 // The test will exercise several relations of sizes. Basic invariants
282 // checked in each case: each dimension in adjusted_size ougth not be greater
283 // than the source image and not lesser than requested target. Aspect ratio
284 // of adjusted_size should never be worse than that of computed_size.
285 gfx::Size target_size(212, 100);
286 gfx::Size image_size(1000, 2000);
287 gfx::Size computed_size(420, 200);
289 gfx::Size adjusted_size = AdjustClippingSizeToAspectRatio(
290 target_size, image_size, computed_size);
292 EXPECT_LE(adjusted_size.width(), image_size.width());
293 EXPECT_LE(adjusted_size.height(), image_size.height());
294 EXPECT_GE(adjusted_size.width(), target_size.width());
295 EXPECT_GE(adjusted_size.height(), target_size.height());
296 EXPECT_LE(AspectDifference(target_size, adjusted_size),
297 AspectDifference(target_size, computed_size));
298 // This case is special (and trivial): no change expected.
299 EXPECT_EQ(computed_size, adjusted_size);
301 // Computed size is too tall. Adjusted size has to add rows.
302 computed_size.SetSize(600, 150);
303 adjusted_size = AdjustClippingSizeToAspectRatio(
304 target_size, image_size, computed_size);
305 // Invariant check.
306 EXPECT_LE(adjusted_size.width(), image_size.width());
307 EXPECT_LE(adjusted_size.height(), image_size.height());
308 EXPECT_GE(adjusted_size.width(), target_size.width());
309 EXPECT_GE(adjusted_size.height(), target_size.height());
310 EXPECT_LE(AspectDifference(target_size, adjusted_size),
311 AspectDifference(target_size, computed_size));
312 // Specific to this case.
313 EXPECT_EQ(computed_size.width(), adjusted_size.width());
314 EXPECT_LE(computed_size.height(), adjusted_size.height());
315 EXPECT_NEAR(
316 static_cast<float>(target_size.width()) / target_size.height(),
317 static_cast<float>(adjusted_size.width()) / adjusted_size.height(),
318 0.02f);
320 // Computed size is too wide. Adjusted size has to add columns.
321 computed_size.SetSize(200, 400);
322 adjusted_size = AdjustClippingSizeToAspectRatio(
323 target_size, image_size, computed_size);
324 // Invariant check.
325 EXPECT_LE(adjusted_size.width(), image_size.width());
326 EXPECT_LE(adjusted_size.height(), image_size.height());
327 EXPECT_GE(adjusted_size.width(), target_size.width());
328 EXPECT_GE(adjusted_size.height(), target_size.height());
329 EXPECT_LE(AspectDifference(target_size, adjusted_size),
330 AspectDifference(target_size, computed_size));
331 EXPECT_NEAR(
332 static_cast<float>(target_size.width()) / target_size.height(),
333 static_cast<float>(adjusted_size.width()) / adjusted_size.height(),
334 0.02f);
336 target_size.SetSize(416, 205);
337 image_size.SetSize(1200, 1200);
338 computed_size.SetSize(900, 300);
339 adjusted_size = AdjustClippingSizeToAspectRatio(
340 target_size, image_size, computed_size);
341 // Invariant check.
342 EXPECT_LE(adjusted_size.width(), image_size.width());
343 EXPECT_LE(adjusted_size.height(), image_size.height());
344 EXPECT_GE(adjusted_size.width(), target_size.width());
345 EXPECT_GE(adjusted_size.height(), target_size.height());
346 EXPECT_LE(AspectDifference(target_size, adjusted_size),
347 AspectDifference(target_size, computed_size));
348 // Specific to this case.
349 EXPECT_EQ(computed_size.width(), adjusted_size.width());
350 EXPECT_LE(computed_size.height(), adjusted_size.height());
351 EXPECT_NEAR(
352 static_cast<float>(target_size.width()) / target_size.height(),
353 static_cast<float>(adjusted_size.width()) / adjusted_size.height(),
354 0.02f);
356 target_size.SetSize(416, 205);
357 image_size.SetSize(1200, 1200);
358 computed_size.SetSize(300, 300);
359 adjusted_size = AdjustClippingSizeToAspectRatio(
360 target_size, image_size, computed_size);
361 // Invariant check.
362 EXPECT_LE(adjusted_size.width(), image_size.width());
363 EXPECT_LE(adjusted_size.height(), image_size.height());
364 EXPECT_GE(adjusted_size.width(), target_size.width());
365 EXPECT_GE(adjusted_size.height(), target_size.height());
366 EXPECT_LE(AspectDifference(target_size, adjusted_size),
367 AspectDifference(target_size, computed_size));
368 // Specific to this case.
369 EXPECT_EQ(computed_size.height(), adjusted_size.height());
370 EXPECT_LE(computed_size.width(), adjusted_size.width());
371 EXPECT_NEAR(
372 static_cast<float>(target_size.width()) / target_size.height(),
373 static_cast<float>(adjusted_size.width()) / adjusted_size.height(),
374 0.02f);
376 computed_size.SetSize(200, 300);
377 adjusted_size = AdjustClippingSizeToAspectRatio(
378 target_size, image_size, computed_size);
379 // Invariant check.
380 EXPECT_LE(adjusted_size.width(), image_size.width());
381 EXPECT_LE(adjusted_size.height(), image_size.height());
382 EXPECT_GE(adjusted_size.width(), target_size.width());
383 EXPECT_GE(adjusted_size.height(), target_size.height());
384 EXPECT_LE(AspectDifference(target_size, adjusted_size),
385 AspectDifference(target_size, computed_size));
386 // Specific to this case.
387 EXPECT_EQ(computed_size.height(), adjusted_size.height());
388 EXPECT_LE(computed_size.width(), adjusted_size.width());
389 EXPECT_NEAR(
390 static_cast<float>(target_size.width()) / target_size.height(),
391 static_cast<float>(adjusted_size.width()) / adjusted_size.height(),
392 0.02f);
394 target_size.SetSize(416, 205);
395 image_size.SetSize(1400, 600);
396 computed_size.SetSize(300, 300);
397 adjusted_size = AdjustClippingSizeToAspectRatio(
398 target_size, image_size, computed_size);
399 // Invariant check.
400 EXPECT_LE(adjusted_size.width(), image_size.width());
401 EXPECT_LE(adjusted_size.height(), image_size.height());
402 EXPECT_GE(adjusted_size.width(), target_size.width());
403 EXPECT_GE(adjusted_size.height(), target_size.height());
404 EXPECT_LE(AspectDifference(target_size, adjusted_size),
405 AspectDifference(target_size, computed_size));
406 // Specific to this case.
407 EXPECT_EQ(computed_size.height(), adjusted_size.height());
408 EXPECT_LE(computed_size.width(), adjusted_size.width());
409 EXPECT_NEAR(
410 static_cast<float>(target_size.width()) / target_size.height(),
411 static_cast<float>(adjusted_size.width()) / adjusted_size.height(),
412 0.02f);
414 computed_size.SetSize(900, 300);
415 adjusted_size = AdjustClippingSizeToAspectRatio(
416 target_size, image_size, computed_size);
417 // Invariant check.
418 EXPECT_LE(adjusted_size.width(), image_size.width());
419 EXPECT_LE(adjusted_size.height(), image_size.height());
420 EXPECT_GE(adjusted_size.width(), target_size.width());
421 EXPECT_GE(adjusted_size.height(), target_size.height());
422 EXPECT_LE(AspectDifference(target_size, adjusted_size),
423 AspectDifference(target_size, computed_size));
424 // Specific to this case.
425 EXPECT_LE(computed_size.height(), adjusted_size.height());
426 EXPECT_NEAR(
427 static_cast<float>(target_size.width()) / target_size.height(),
428 static_cast<float>(adjusted_size.width()) / adjusted_size.height(),
429 0.02f);
432 TEST_F(ThumbnailContentAnalysisTest, AutoSegmentPeaks) {
433 std::vector<float> profile_info;
435 EXPECT_EQ(AutoSegmentPeaks(profile_info), std::numeric_limits<float>::max());
436 profile_info.resize(1000, 1.0f);
437 EXPECT_EQ(AutoSegmentPeaks(profile_info), 1.0f);
438 std::srand(42);
439 std::generate(profile_info.begin(), profile_info.end(), std::rand);
440 float threshold = AutoSegmentPeaks(profile_info);
441 EXPECT_GT(threshold, 0); // Not much to expect.
443 // There should be roughly 50% above and below the threshold.
444 // Random is not really random thanks to srand, so we can sort-of compare.
445 int above_count = std::count_if(
446 profile_info.begin(),
447 profile_info.end(),
448 std::bind2nd(std::greater<float>(), threshold));
449 EXPECT_GT(above_count, 450); // Not much to expect.
450 EXPECT_LT(above_count, 550);
452 for (unsigned i = 0; i < profile_info.size(); ++i) {
453 float y = std::sin(M_PI * i / 250.0f);
454 profile_info[i] = y > 0 ? y : 0;
456 threshold = AutoSegmentPeaks(profile_info);
458 above_count = std::count_if(
459 profile_info.begin(),
460 profile_info.end(),
461 std::bind2nd(std::greater<float>(), threshold));
462 EXPECT_LT(above_count, 500); // Negative y expected to fall below threshold.
464 // Expect two peaks around between 0 and 250 and 500 and 750.
465 std::vector<bool> thresholded_values(profile_info.size(), false);
466 std::transform(profile_info.begin(),
467 profile_info.end(),
468 thresholded_values.begin(),
469 std::bind2nd(std::greater<float>(), threshold));
470 EXPECT_TRUE(thresholded_values[125]);
471 EXPECT_TRUE(thresholded_values[625]);
472 int transitions = 0;
473 for (unsigned i = 1; i < thresholded_values.size(); ++i) {
474 if (thresholded_values[i] != thresholded_values[i-1])
475 transitions++;
477 EXPECT_EQ(transitions, 4); // We have two contiguous peaks. Good going!
480 TEST_F(ThumbnailContentAnalysisTest, ConstrainedProfileSegmentation) {
481 const size_t kRowCount = 800;
482 const size_t kColumnCount = 1400;
483 const gfx::Size target_size(300, 150);
484 std::vector<float> rows_profile(kRowCount);
485 std::vector<float> columns_profile(kColumnCount);
487 std::srand(42);
488 std::generate(rows_profile.begin(), rows_profile.end(), std::rand);
489 std::generate(columns_profile.begin(), columns_profile.end(), std::rand);
491 // Bring noise level to 0-1.
492 std::transform(rows_profile.begin(),
493 rows_profile.end(),
494 rows_profile.begin(),
495 std::bind2nd(std::divides<float>(), RAND_MAX));
496 std::transform(columns_profile.begin(),
497 columns_profile.end(),
498 columns_profile.begin(),
499 std::bind2nd(std::divides<float>(), RAND_MAX));
501 // Set up values to 0-1.
502 std::transform(rows_profile.begin(),
503 rows_profile.end(),
504 rows_profile.begin(),
505 std::bind2nd(std::plus<float>(), 1.0f));
506 std::transform(columns_profile.begin(),
507 columns_profile.end(),
508 columns_profile.begin(),
509 std::bind2nd(std::plus<float>(), 1.0f));
511 std::transform(rows_profile.begin() + 300,
512 rows_profile.begin() + 450,
513 rows_profile.begin() + 300,
514 std::bind2nd(std::plus<float>(), 8.0f));
515 std::transform(columns_profile.begin() + 400,
516 columns_profile.begin() + 1000,
517 columns_profile.begin() + 400,
518 std::bind2nd(std::plus<float>(), 10.0f));
520 // Make sure that threshold falls somewhere reasonable.
521 float row_threshold = AutoSegmentPeaks(rows_profile);
522 EXPECT_GT(row_threshold, 1.0f);
523 EXPECT_LT(row_threshold, 9.0f);
525 int row_above_count = std::count_if(
526 rows_profile.begin(),
527 rows_profile.end(),
528 std::bind2nd(std::greater<float>(), row_threshold));
529 EXPECT_EQ(row_above_count, 150);
531 float column_threshold = AutoSegmentPeaks(columns_profile);
532 EXPECT_GT(column_threshold, 1.0f);
533 EXPECT_LT(column_threshold, 11.0f);
535 int column_above_count = std::count_if(
536 columns_profile.begin(),
537 columns_profile.end(),
538 std::bind2nd(std::greater<float>(), column_threshold));
539 EXPECT_EQ(column_above_count, 600);
542 std::vector<bool> rows_guide;
543 std::vector<bool> columns_guide;
544 ConstrainedProfileSegmentation(
545 rows_profile, columns_profile, target_size, &rows_guide, &columns_guide);
547 int row_count = std::count(rows_guide.begin(), rows_guide.end(), true);
548 int column_count = std::count(
549 columns_guide.begin(), columns_guide.end(), true);
550 float expected_aspect =
551 static_cast<float>(target_size.width()) / target_size.height();
552 float actual_aspect = static_cast<float>(column_count) / row_count;
553 EXPECT_GE(1.05f, expected_aspect / actual_aspect);
554 EXPECT_GE(1.05f, actual_aspect / expected_aspect);
557 TEST_F(ThumbnailContentAnalysisTest, ComputeDecimatedImage) {
558 gfx::Size image_size(1600, 1200);
559 gfx::Canvas canvas(image_size, 1.0f, true);
561 // Make some content we will later want to keep.
562 canvas.FillRect(gfx::Rect(100, 200, 100, 100), SkColorSetRGB(125, 0, 0));
563 canvas.FillRect(gfx::Rect(300, 200, 100, 100), SkColorSetRGB(0, 200, 0));
564 canvas.FillRect(gfx::Rect(500, 200, 100, 100), SkColorSetRGB(0, 0, 225));
565 canvas.FillRect(gfx::Rect(100, 400, 600, 100), SkColorSetRGB(125, 200, 225));
567 std::vector<bool> rows(image_size.height(), false);
568 std::fill_n(rows.begin() + 200, 100, true);
569 std::fill_n(rows.begin() + 400, 100, true);
571 std::vector<bool> columns(image_size.width(), false);
572 std::fill_n(columns.begin() + 100, 100, true);
573 std::fill_n(columns.begin() + 300, 100, true);
574 std::fill_n(columns.begin() + 500, 100, true);
576 SkBitmap source =
577 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
578 SkBitmap result = ComputeDecimatedImage(source, rows, columns);
579 EXPECT_FALSE(result.empty());
580 EXPECT_EQ(300, result.width());
581 EXPECT_EQ(200, result.height());
583 // The call should have removed all empty spaces.
584 ASSERT_TRUE(CompareImageFragments(source,
585 result,
586 gfx::Size(100, 100),
587 gfx::Point(100, 200),
588 gfx::Point(0, 0)));
589 ASSERT_TRUE(CompareImageFragments(source,
590 result,
591 gfx::Size(100, 100),
592 gfx::Point(300, 200),
593 gfx::Point(100, 0)));
594 ASSERT_TRUE(CompareImageFragments(source,
595 result,
596 gfx::Size(100, 100),
597 gfx::Point(500, 200),
598 gfx::Point(200, 0)));
599 ASSERT_TRUE(CompareImageFragments(source,
600 result,
601 gfx::Size(100, 100),
602 gfx::Point(100, 400),
603 gfx::Point(0, 100)));
606 TEST_F(ThumbnailContentAnalysisTest, CreateRetargetedThumbnailImage) {
607 gfx::Size image_size(1200, 1300);
608 gfx::Canvas canvas(image_size, 1.0f, true);
610 // The following will create a 'fake image' consisting of color blocks placed
611 // on a neutral background. The entire layout is supposed to mimic a
612 // screenshot of a web page.
613 // The tested function is supposed to locate the interesing areas in the
614 // middle.
615 const int margin_horizontal = 60;
616 const int margin_vertical = 20;
617 canvas.FillRect(gfx::Rect(image_size), SkColorSetRGB(200, 210, 210));
618 const gfx::Rect header_rect(margin_horizontal,
619 margin_vertical,
620 image_size.width() - 2 * margin_horizontal,
621 100);
622 const gfx::Rect footer_rect(margin_horizontal,
623 image_size.height() - margin_vertical - 100,
624 image_size.width() - 2 * margin_horizontal,
625 100);
626 const gfx::Rect body_rect(margin_horizontal,
627 header_rect.bottom() + margin_vertical,
628 image_size.width() - 2 * margin_horizontal,
629 footer_rect.y() - header_rect.bottom() -
630 2 * margin_vertical);
631 canvas.FillRect(header_rect, SkColorSetRGB(200, 40, 10));
632 canvas.FillRect(footer_rect, SkColorSetRGB(10, 40, 180));
633 canvas.FillRect(body_rect, SkColorSetRGB(150, 180, 40));
635 // 'Fine print' at the bottom.
636 const int fine_print = 8;
637 const SkColor print_color = SkColorSetRGB(45, 30, 30);
638 for (int y = footer_rect.y() + fine_print;
639 y < footer_rect.bottom() - fine_print;
640 y += 2 * fine_print) {
641 for (int x = footer_rect.x() + fine_print;
642 x < footer_rect.right() - fine_print;
643 x += 2 * fine_print) {
644 canvas.DrawRect(gfx::Rect(x, y, fine_print, fine_print), print_color);
648 // Blocky content at the top.
649 const int block_size = header_rect.height() - margin_vertical;
650 for (int x = header_rect.x() + margin_horizontal;
651 x < header_rect.right() - block_size;
652 x += block_size + margin_horizontal) {
653 const int half_block = block_size / 2 - 5;
654 const SkColor block_color = SkColorSetRGB(255, 255, 255);
655 const int y = header_rect.y() + margin_vertical / 2;
656 int second_col = x + half_block + 10;
657 int second_row = y + half_block + 10;
658 canvas.FillRect(gfx::Rect(x, y, half_block, block_size), block_color);
659 canvas.FillRect(gfx::Rect(second_col, y, half_block, half_block),
660 block_color);
661 canvas.FillRect(gfx::Rect(second_col, second_row, half_block, half_block),
662 block_color);
665 // Now the main body. Mostly text with some 'pictures'.
666 for (int y = body_rect.y() + fine_print;
667 y < body_rect.bottom() - fine_print;
668 y += 2 * fine_print) {
669 for (int x = body_rect.x() + fine_print;
670 x < body_rect.right() - fine_print;
671 x += 2 * fine_print) {
672 canvas.DrawRect(gfx::Rect(x, y, fine_print, fine_print), print_color);
676 for (int line = 0; line < 3; ++line) {
677 int alignment = line % 2;
678 const int y = body_rect.y() +
679 body_rect.height() / 3 * line + margin_vertical;
680 const int x = body_rect.x() +
681 alignment * body_rect.width() / 2 + margin_vertical;
682 gfx::Rect pict_rect(x, y,
683 body_rect.width() / 2 - 2 * margin_vertical,
684 body_rect.height() / 3 - 2 * margin_vertical);
685 canvas.FillRect(pict_rect, SkColorSetRGB(255, 255, 255));
686 canvas.DrawRect(pict_rect, SkColorSetRGB(0, 0, 0));
689 SkBitmap source =
690 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
692 SkBitmap result = CreateRetargetedThumbnailImage(
693 source, gfx::Size(424, 264), 2.5);
694 EXPECT_FALSE(result.empty());
696 // Given the nature of computation We can't really assert much here about the
697 // image itself. We know it should have been computed, should be smaller than
698 // the original and it must not be zero.
699 EXPECT_LT(result.width(), image_size.width());
700 EXPECT_LT(result.height(), image_size.height());
702 int histogram[256] = {};
703 color_utils::BuildLumaHistogram(result, histogram);
704 int non_zero_color_count = std::count_if(
705 histogram, histogram + 256, std::bind2nd(std::greater<int>(), 0));
706 EXPECT_GT(non_zero_color_count, 4);
710 } // namespace thumbnailing_utils