1c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// found in the LICENSE file.
4c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/thumbnails/content_analysis.h"
6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
7c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <algorithm>
8c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <cmath>
9c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <cstdlib>
10c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <functional>
11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <limits>
12c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <numeric>
13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <vector>
14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/memory/scoped_ptr.h"
16c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "testing/gtest/include/gtest/gtest.h"
17c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "third_party/skia/include/core/SkBitmap.h"
18c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "third_party/skia/include/core/SkColor.h"
19c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/gfx/canvas.h"
20c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/gfx/color_analysis.h"
21c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/gfx/color_utils.h"
22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/gfx/image/image.h"
23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/gfx/rect.h"
24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/gfx/size.h"
25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace {
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#ifndef M_PI
29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#define M_PI 3.14159265358979323846
30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#endif
31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
32c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)unsigned long ImagePixelSum(const SkBitmap& bitmap, const gfx::Rect& rect) {
33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Get the sum of pixel values in the rectangle. Applicable only to
34c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // monochrome bitmaps.
35116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  DCHECK_EQ(kAlpha_8_SkColorType, bitmap.colorType());
36c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  unsigned long total = 0;
37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (int r = rect.y(); r < rect.bottom(); ++r) {
38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const uint8* row_data = static_cast<const uint8*>(
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        bitmap.getPixels()) + r * bitmap.rowBytes();
40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for (int c = rect.x(); c < rect.right(); ++c)
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      total += row_data[c];
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
43c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return total;
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool CompareImageFragments(const SkBitmap& bitmap_left,
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                           const SkBitmap& bitmap_right,
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                           const gfx::Size& comparison_area,
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                           const gfx::Point& origin_left,
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                           const gfx::Point& origin_right) {
52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SkAutoLockPixels left_lock(bitmap_left);
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SkAutoLockPixels right_lock(bitmap_right);
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (int r = 0; r < comparison_area.height(); ++r) {
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for (int c = 0; c < comparison_area.width(); ++c) {
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      SkColor color_left = bitmap_left.getColor(origin_left.x() + c,
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                                origin_left.y() + r);
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      SkColor color_right = bitmap_right.getColor(origin_right.x() + c,
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                                  origin_right.y() + r);
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (color_left != color_right)
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        return false;
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return true;
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
68868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)float AspectDifference(const gfx::Size& reference, const gfx::Size& candidate) {
69868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return std::abs(static_cast<float>(candidate.width()) / candidate.height() -
70868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                  static_cast<float>(reference.width()) / reference.height());
71868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
72868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}  // namespace
74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace thumbnailing_utils {
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class ThumbnailContentAnalysisTest : public testing::Test {
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)};
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
80c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)TEST_F(ThumbnailContentAnalysisTest, ApplyGradientMagnitudeOnImpulse) {
8168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  gfx::Canvas canvas(gfx::Size(800, 600), 1.0f, true);
82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // The image consists of a point spike on uniform (non-zero) background.
844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  canvas.FillRect(gfx::Rect(0, 0, 800, 600), SkColorSetRGB(10, 10, 10));
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  canvas.FillRect(gfx::Rect(400, 300, 1, 1), SkColorSetRGB(255, 255, 255));
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
87c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SkBitmap source =
88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
89c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SkBitmap reduced_color;
91116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  reduced_color.allocPixels(SkImageInfo::MakeA8(source.width(),
92116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                                source.height()));
93c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::Vector3dF transform(0.299f, 0.587f, 0.114f);
95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_TRUE(color_utils::ApplyColorReduction(
96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      source, transform, true, &reduced_color));
97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  float sigma = 2.5f;
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ApplyGaussianGradientMagnitudeFilter(&reduced_color, sigma);
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Expect everything to be within 8 * sigma.
102c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int tail_length = static_cast<int>(8.0f * sigma + 0.5f);
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::Rect echo_rect(399 - tail_length, 299 - tail_length,
104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                      2 * tail_length + 1, 2 * tail_length + 1);
105c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  unsigned long data_sum = ImagePixelSum(reduced_color, echo_rect);
106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  unsigned long all_sum = ImagePixelSum(reduced_color, gfx::Rect(800, 600));
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_GT(data_sum, 0U);
108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(data_sum, all_sum);
109c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
110c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  sigma = 5.0f;
111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ApplyGaussianGradientMagnitudeFilter(&reduced_color, sigma);
112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
113c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Expect everything to be within 8 * sigma.
114c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  tail_length = static_cast<int>(8.0f * sigma + 0.5f);
115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  echo_rect = gfx::Rect(399 - tail_length, 299 - tail_length,
116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                        2 * tail_length + 1, 2 * tail_length + 1);
117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  data_sum = ImagePixelSum(reduced_color, echo_rect);
118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  all_sum = ImagePixelSum(reduced_color, gfx::Rect(800, 600));
119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_GT(data_sum, 0U);
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(data_sum, all_sum);
121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
1234e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)TEST_F(ThumbnailContentAnalysisTest, ApplyGradientMagnitudeOnFrame) {
12468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  gfx::Canvas canvas(gfx::Size(800, 600), 1.0f, true);
125c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
126c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // The image consists of a single white block in the centre.
127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::Rect draw_rect(300, 200, 200, 200);
1284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  canvas.FillRect(gfx::Rect(0, 0, 800, 600), SkColorSetRGB(0, 0, 0));
129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  canvas.DrawRect(draw_rect, SkColorSetRGB(255, 255, 255));
130c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
131c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SkBitmap source =
132c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
133c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
134c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SkBitmap reduced_color;
135116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  reduced_color.allocPixels(SkImageInfo::MakeA8(source.width(),
136116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                                source.height()));
137c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
138c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::Vector3dF transform(0.299f, 0.587f, 0.114f);
139c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_TRUE(color_utils::ApplyColorReduction(
140c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      source, transform, true, &reduced_color));
141c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
142c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  float sigma = 2.5f;
143c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ApplyGaussianGradientMagnitudeFilter(&reduced_color, sigma);
144c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
145c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int tail_length = static_cast<int>(8.0f * sigma + 0.5f);
146c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::Rect outer_rect(draw_rect.x() - tail_length,
147c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                       draw_rect.y() - tail_length,
148c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                       draw_rect.width() + 2 * tail_length,
149c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                       draw_rect.height() + 2 * tail_length);
150c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::Rect inner_rect(draw_rect.x() + tail_length,
151c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                       draw_rect.y() + tail_length,
152c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                       draw_rect.width() - 2 * tail_length,
153c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                       draw_rect.height() - 2 * tail_length);
154c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  unsigned long data_sum = ImagePixelSum(reduced_color, outer_rect);
155c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  unsigned long all_sum = ImagePixelSum(reduced_color, gfx::Rect(800, 600));
156c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_GT(data_sum, 0U);
157c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(data_sum, all_sum);
158c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(ImagePixelSum(reduced_color, inner_rect), 0U);
159c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
160c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
161c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)TEST_F(ThumbnailContentAnalysisTest, ExtractImageProfileInformation) {
16268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  gfx::Canvas canvas(gfx::Size(800, 600), 1.0f, true);
163c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
164c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // The image consists of a white frame drawn in the centre.
165c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::Rect draw_rect(100, 100, 200, 100);
166c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::Rect image_rect(0, 0, 800, 600);
1674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  canvas.FillRect(image_rect, SkColorSetRGB(0, 0, 0));
168c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  canvas.DrawRect(draw_rect, SkColorSetRGB(255, 255, 255));
169c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
170c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SkBitmap source =
171c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
172c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SkBitmap reduced_color;
173116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  reduced_color.allocPixels(SkImageInfo::MakeA8(source.width(),
174116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                                source.height()));
175c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
176c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::Vector3dF transform(1, 0, 0);
177c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_TRUE(color_utils::ApplyColorReduction(
178c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      source, transform, true, &reduced_color));
179c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<float> column_profile;
180c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<float> row_profile;
181c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ExtractImageProfileInformation(reduced_color,
182c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 image_rect,
183c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 gfx::Size(),
184c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 false,
185c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 &row_profile,
186c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 &column_profile);
187c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(0, std::accumulate(column_profile.begin(),
188c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                               column_profile.begin() + draw_rect.x() - 1,
189c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                               0));
190c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(column_profile[draw_rect.x()], 255U * (draw_rect.height() + 1));
191c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(2 * 255 * (draw_rect.width() - 2),
192c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            std::accumulate(column_profile.begin() + draw_rect.x() + 1,
193c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                            column_profile.begin() + draw_rect.right() - 1,
194c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                            0));
195c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
196c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(0, std::accumulate(row_profile.begin(),
197c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                               row_profile.begin() + draw_rect.y() - 1,
198c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                               0));
199c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(row_profile[draw_rect.y()], 255U * (draw_rect.width() + 1));
200c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(2 * 255 * (draw_rect.height() - 2),
201c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            std::accumulate(row_profile.begin() + draw_rect.y() + 1,
202c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                            row_profile.begin() + draw_rect.bottom() - 1,
203c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                            0));
204c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
205c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::Rect test_rect(150, 80, 400, 100);
206c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ExtractImageProfileInformation(reduced_color,
207c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 test_rect,
208c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 gfx::Size(),
209c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 false,
210c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 &row_profile,
211c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 &column_profile);
212c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
213c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Some overlap with the drawn rectagle. If you work it out on a piece of
214c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // paper, sums should be as follows.
215c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(255 * (test_rect.bottom() - draw_rect.y()) +
216c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            255 * (draw_rect.right() - test_rect.x()),
217c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            std::accumulate(row_profile.begin(), row_profile.end(), 0));
218c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(255 * (test_rect.bottom() - draw_rect.y()) +
219c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            255 * (draw_rect.right() - test_rect.x()),
220c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            std::accumulate(column_profile.begin(), column_profile.end(), 0));
221c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
222c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
223c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)TEST_F(ThumbnailContentAnalysisTest,
2244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)       ExtractImageProfileInformationWithClosing) {
22568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  gfx::Canvas canvas(gfx::Size(800, 600), 1.0f, true);
226c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
227c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // The image consists of a two white frames drawn side by side, with a
228c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // single-pixel vertical gap in between.
229c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::Rect image_rect(0, 0, 800, 600);
2304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  canvas.FillRect(image_rect, SkColorSetRGB(0, 0, 0));
231c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  canvas.DrawRect(gfx::Rect(300, 250, 99, 100), SkColorSetRGB(255, 255, 255));
232c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  canvas.DrawRect(gfx::Rect(401, 250, 99, 100), SkColorSetRGB(255, 255, 255));
233c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
234c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SkBitmap source =
235c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
236c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SkBitmap reduced_color;
237116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  reduced_color.allocPixels(SkImageInfo::MakeA8(source.width(),
238116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                                source.height()));
239c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
240c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::Vector3dF transform(1, 0, 0);
241c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_TRUE(color_utils::ApplyColorReduction(
242c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      source, transform, true, &reduced_color));
243c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<float> column_profile;
244c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<float> row_profile;
245c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
246c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ExtractImageProfileInformation(reduced_color,
247c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 image_rect,
248c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 gfx::Size(),
249c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 true,
250c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 &row_profile,
251c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 &column_profile);
252c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Column profiles should have two spikes in the middle, with a single
253c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // 0-valued value between them.
254c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_GT(column_profile[398], 0.0f);
255c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_GT(column_profile[399], column_profile[398]);
256c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_GT(column_profile[402], 0.0f);
257c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_GT(column_profile[401], column_profile[402]);
258c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(column_profile[401], column_profile[399]);
259c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(column_profile[402], column_profile[398]);
260c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(column_profile[400], 0.0f);
261c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(column_profile[299], 0.0f);
262c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(column_profile[502], 0.0f);
263c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
264c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Now the same with closing applied. The space in the middle will be closed.
265c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ExtractImageProfileInformation(reduced_color,
266c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 image_rect,
267c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 gfx::Size(200, 100),
268c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 true,
269c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 &row_profile,
270c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 &column_profile);
271c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_GT(column_profile[398], 0);
272c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_GT(column_profile[400], 0);
273c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_GT(column_profile[402], 0);
274c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(column_profile[299], 0);
275c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(column_profile[502], 0);
276c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(column_profile[399], column_profile[401]);
277c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(column_profile[398], column_profile[402]);
278c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
279c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
280868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)TEST_F(ThumbnailContentAnalysisTest, AdjustClippingSizeToAspectRatio) {
281868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // The test will exercise several relations of sizes. Basic invariants
282868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // checked in each case: each dimension in adjusted_size ougth not be greater
283868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // than the source image and not lesser than requested target. Aspect ratio
284868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // of adjusted_size should never be worse than that of computed_size.
285868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  gfx::Size target_size(212, 100);
286868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  gfx::Size image_size(1000, 2000);
287868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  gfx::Size computed_size(420, 200);
288868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
289868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  gfx::Size adjusted_size = AdjustClippingSizeToAspectRatio(
290868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      target_size, image_size, computed_size);
291868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
292868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(adjusted_size.width(), image_size.width());
293868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(adjusted_size.height(), image_size.height());
294868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_GE(adjusted_size.width(), target_size.width());
295868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_GE(adjusted_size.height(), target_size.height());
296868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(AspectDifference(target_size, adjusted_size),
297868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)            AspectDifference(target_size, computed_size));
298868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // This case is special (and trivial): no change expected.
299868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_EQ(computed_size, adjusted_size);
300868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
301868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Computed size is too tall. Adjusted size has to add rows.
302868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  computed_size.SetSize(600, 150);
303868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  adjusted_size = AdjustClippingSizeToAspectRatio(
304868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      target_size, image_size, computed_size);
305868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Invariant check.
306868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(adjusted_size.width(), image_size.width());
307868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(adjusted_size.height(), image_size.height());
308868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_GE(adjusted_size.width(), target_size.width());
309868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_GE(adjusted_size.height(), target_size.height());
310868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(AspectDifference(target_size, adjusted_size),
311868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)            AspectDifference(target_size, computed_size));
312868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Specific to this case.
313868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_EQ(computed_size.width(), adjusted_size.width());
314868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(computed_size.height(), adjusted_size.height());
315868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_NEAR(
316868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      static_cast<float>(target_size.width()) / target_size.height(),
317868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      static_cast<float>(adjusted_size.width()) / adjusted_size.height(),
318868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      0.02f);
319868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
320868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Computed size is too wide. Adjusted size has to add columns.
321868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  computed_size.SetSize(200, 400);
322868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  adjusted_size = AdjustClippingSizeToAspectRatio(
323868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      target_size, image_size, computed_size);
324868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Invariant check.
325868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(adjusted_size.width(), image_size.width());
326868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(adjusted_size.height(), image_size.height());
327868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_GE(adjusted_size.width(), target_size.width());
328868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_GE(adjusted_size.height(), target_size.height());
329868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(AspectDifference(target_size, adjusted_size),
330868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)            AspectDifference(target_size, computed_size));
331868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_NEAR(
332868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      static_cast<float>(target_size.width()) / target_size.height(),
333868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      static_cast<float>(adjusted_size.width()) / adjusted_size.height(),
334868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      0.02f);
335868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
336868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  target_size.SetSize(416, 205);
337868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  image_size.SetSize(1200, 1200);
338868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  computed_size.SetSize(900, 300);
339868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  adjusted_size = AdjustClippingSizeToAspectRatio(
340868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      target_size, image_size, computed_size);
341868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Invariant check.
342868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(adjusted_size.width(), image_size.width());
343868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(adjusted_size.height(), image_size.height());
344868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_GE(adjusted_size.width(), target_size.width());
345868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_GE(adjusted_size.height(), target_size.height());
346868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(AspectDifference(target_size, adjusted_size),
347868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)            AspectDifference(target_size, computed_size));
348868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Specific to this case.
349868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_EQ(computed_size.width(), adjusted_size.width());
350868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(computed_size.height(), adjusted_size.height());
351868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_NEAR(
352868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      static_cast<float>(target_size.width()) / target_size.height(),
353868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      static_cast<float>(adjusted_size.width()) / adjusted_size.height(),
354868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      0.02f);
355868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
356868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  target_size.SetSize(416, 205);
357868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  image_size.SetSize(1200, 1200);
358868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  computed_size.SetSize(300, 300);
359868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  adjusted_size = AdjustClippingSizeToAspectRatio(
360868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      target_size, image_size, computed_size);
361868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Invariant check.
362868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(adjusted_size.width(), image_size.width());
363868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(adjusted_size.height(), image_size.height());
364868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_GE(adjusted_size.width(), target_size.width());
365868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_GE(adjusted_size.height(), target_size.height());
366868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(AspectDifference(target_size, adjusted_size),
367868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)            AspectDifference(target_size, computed_size));
368868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Specific to this case.
369868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_EQ(computed_size.height(), adjusted_size.height());
370868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(computed_size.width(), adjusted_size.width());
371868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_NEAR(
372868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      static_cast<float>(target_size.width()) / target_size.height(),
373868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      static_cast<float>(adjusted_size.width()) / adjusted_size.height(),
374868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      0.02f);
375868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
376868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  computed_size.SetSize(200, 300);
377868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  adjusted_size = AdjustClippingSizeToAspectRatio(
378868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      target_size, image_size, computed_size);
379868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Invariant check.
380868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(adjusted_size.width(), image_size.width());
381868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(adjusted_size.height(), image_size.height());
382868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_GE(adjusted_size.width(), target_size.width());
383868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_GE(adjusted_size.height(), target_size.height());
384868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(AspectDifference(target_size, adjusted_size),
385868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)            AspectDifference(target_size, computed_size));
386868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Specific to this case.
387868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_EQ(computed_size.height(), adjusted_size.height());
388868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(computed_size.width(), adjusted_size.width());
389868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_NEAR(
390868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      static_cast<float>(target_size.width()) / target_size.height(),
391868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      static_cast<float>(adjusted_size.width()) / adjusted_size.height(),
392868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      0.02f);
393868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
394868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  target_size.SetSize(416, 205);
395868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  image_size.SetSize(1400, 600);
396868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  computed_size.SetSize(300, 300);
397868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  adjusted_size = AdjustClippingSizeToAspectRatio(
398868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      target_size, image_size, computed_size);
399868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Invariant check.
400868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(adjusted_size.width(), image_size.width());
401868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(adjusted_size.height(), image_size.height());
402868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_GE(adjusted_size.width(), target_size.width());
403868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_GE(adjusted_size.height(), target_size.height());
404868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(AspectDifference(target_size, adjusted_size),
405868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)            AspectDifference(target_size, computed_size));
406868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Specific to this case.
407868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_EQ(computed_size.height(), adjusted_size.height());
408868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(computed_size.width(), adjusted_size.width());
409868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_NEAR(
410868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      static_cast<float>(target_size.width()) / target_size.height(),
411868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      static_cast<float>(adjusted_size.width()) / adjusted_size.height(),
412868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      0.02f);
413868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
414868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  computed_size.SetSize(900, 300);
415868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  adjusted_size = AdjustClippingSizeToAspectRatio(
416868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      target_size, image_size, computed_size);
417868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Invariant check.
418868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(adjusted_size.width(), image_size.width());
419868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(adjusted_size.height(), image_size.height());
420868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_GE(adjusted_size.width(), target_size.width());
421868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_GE(adjusted_size.height(), target_size.height());
422868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(AspectDifference(target_size, adjusted_size),
423868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)            AspectDifference(target_size, computed_size));
424868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Specific to this case.
425868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LE(computed_size.height(), adjusted_size.height());
426868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_NEAR(
427868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      static_cast<float>(target_size.width()) / target_size.height(),
428868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      static_cast<float>(adjusted_size.width()) / adjusted_size.height(),
429868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      0.02f);
430868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
431868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
432c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)TEST_F(ThumbnailContentAnalysisTest, AutoSegmentPeaks) {
433c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<float> profile_info;
434c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
435c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(AutoSegmentPeaks(profile_info), std::numeric_limits<float>::max());
436c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  profile_info.resize(1000, 1.0f);
437c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(AutoSegmentPeaks(profile_info), 1.0f);
438c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::srand(42);
439c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::generate(profile_info.begin(), profile_info.end(), std::rand);
440c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  float threshold = AutoSegmentPeaks(profile_info);
441c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_GT(threshold, 0);  // Not much to expect.
442c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
443c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // There should be roughly 50% above and below the threshold.
444c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Random is not really random thanks to srand, so we can sort-of compare.
445c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int above_count = std::count_if(
446c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      profile_info.begin(),
447c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      profile_info.end(),
448c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      std::bind2nd(std::greater<float>(), threshold));
449c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_GT(above_count, 450);  // Not much to expect.
450c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_LT(above_count, 550);
451c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
452c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (unsigned i = 0; i < profile_info.size(); ++i) {
453c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    float y = std::sin(M_PI * i / 250.0f);
454c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    profile_info[i] = y > 0 ? y : 0;
455c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
456c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  threshold = AutoSegmentPeaks(profile_info);
457c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
458c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  above_count = std::count_if(
459c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      profile_info.begin(),
460c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      profile_info.end(),
461c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      std::bind2nd(std::greater<float>(), threshold));
462c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_LT(above_count, 500);  // Negative y expected to fall below threshold.
463c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
464c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Expect two peaks around between 0 and 250 and 500 and 750.
465c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<bool> thresholded_values(profile_info.size(), false);
466c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::transform(profile_info.begin(),
467c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 profile_info.end(),
468c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 thresholded_values.begin(),
469c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 std::bind2nd(std::greater<float>(), threshold));
470c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_TRUE(thresholded_values[125]);
471c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_TRUE(thresholded_values[625]);
472c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int transitions = 0;
473c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (unsigned i = 1; i < thresholded_values.size(); ++i) {
474c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (thresholded_values[i] != thresholded_values[i-1])
475c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      transitions++;
476c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
477c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(transitions, 4);  // We have two contiguous peaks. Good going!
478c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
479c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
480868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)TEST_F(ThumbnailContentAnalysisTest, ConstrainedProfileSegmentation) {
481868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  const size_t kRowCount = 800;
482868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  const size_t kColumnCount = 1400;
483868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  const gfx::Size target_size(300, 150);
484868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  std::vector<float> rows_profile(kRowCount);
485868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  std::vector<float> columns_profile(kColumnCount);
486868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
487868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  std::srand(42);
488868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  std::generate(rows_profile.begin(), rows_profile.end(), std::rand);
489868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  std::generate(columns_profile.begin(), columns_profile.end(), std::rand);
490868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
491868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Bring noise level to 0-1.
492868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  std::transform(rows_profile.begin(),
493868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                 rows_profile.end(),
494868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                 rows_profile.begin(),
495868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                 std::bind2nd(std::divides<float>(), RAND_MAX));
496868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  std::transform(columns_profile.begin(),
497868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                 columns_profile.end(),
498868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                 columns_profile.begin(),
499868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                 std::bind2nd(std::divides<float>(), RAND_MAX));
500868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
501868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Set up values to 0-1.
502868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  std::transform(rows_profile.begin(),
503868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                 rows_profile.end(),
504868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                 rows_profile.begin(),
505868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                 std::bind2nd(std::plus<float>(), 1.0f));
506868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  std::transform(columns_profile.begin(),
507868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                 columns_profile.end(),
508868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                 columns_profile.begin(),
509868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                 std::bind2nd(std::plus<float>(), 1.0f));
510868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
511868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  std::transform(rows_profile.begin() + 300,
512868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                 rows_profile.begin() + 450,
513868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                 rows_profile.begin() + 300,
514868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                 std::bind2nd(std::plus<float>(), 8.0f));
515868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  std::transform(columns_profile.begin() + 400,
516868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                 columns_profile.begin() + 1000,
517868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                 columns_profile.begin() + 400,
518868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                 std::bind2nd(std::plus<float>(), 10.0f));
519868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
520868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Make sure that threshold falls somewhere reasonable.
521868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  float row_threshold = AutoSegmentPeaks(rows_profile);
522868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_GT(row_threshold, 1.0f);
523868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LT(row_threshold, 9.0f);
524868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
525868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  int row_above_count = std::count_if(
526868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      rows_profile.begin(),
527868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      rows_profile.end(),
528868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      std::bind2nd(std::greater<float>(), row_threshold));
529868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_EQ(row_above_count, 150);
530868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
531868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  float column_threshold = AutoSegmentPeaks(columns_profile);
532868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_GT(column_threshold, 1.0f);
533868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_LT(column_threshold, 11.0f);
534868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
535868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  int column_above_count = std::count_if(
536868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      columns_profile.begin(),
537868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      columns_profile.end(),
538868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      std::bind2nd(std::greater<float>(), column_threshold));
539868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_EQ(column_above_count, 600);
540868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
541868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
542868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  std::vector<bool> rows_guide;
543868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  std::vector<bool> columns_guide;
544868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  ConstrainedProfileSegmentation(
545868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      rows_profile, columns_profile, target_size, &rows_guide, &columns_guide);
546868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
547868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  int row_count = std::count(rows_guide.begin(), rows_guide.end(), true);
548868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  int column_count = std::count(
549868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      columns_guide.begin(), columns_guide.end(), true);
550868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  float expected_aspect =
551868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      static_cast<float>(target_size.width()) / target_size.height();
552868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  float actual_aspect = static_cast<float>(column_count) / row_count;
553868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_GE(1.05f, expected_aspect / actual_aspect);
554868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  EXPECT_GE(1.05f, actual_aspect / expected_aspect);
555868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
556868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
557c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)TEST_F(ThumbnailContentAnalysisTest, ComputeDecimatedImage) {
558c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::Size image_size(1600, 1200);
55968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  gfx::Canvas canvas(image_size, 1.0f, true);
560c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
561c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Make some content we will later want to keep.
5624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  canvas.FillRect(gfx::Rect(100, 200, 100, 100), SkColorSetRGB(125, 0, 0));
5634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  canvas.FillRect(gfx::Rect(300, 200, 100, 100), SkColorSetRGB(0, 200, 0));
5644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  canvas.FillRect(gfx::Rect(500, 200, 100, 100), SkColorSetRGB(0, 0, 225));
5654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  canvas.FillRect(gfx::Rect(100, 400, 600, 100), SkColorSetRGB(125, 200, 225));
566c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
567c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<bool> rows(image_size.height(), false);
568c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::fill_n(rows.begin() + 200, 100, true);
569c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::fill_n(rows.begin() + 400, 100, true);
570c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
571c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<bool> columns(image_size.width(), false);
572c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::fill_n(columns.begin() + 100, 100, true);
573c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::fill_n(columns.begin() + 300, 100, true);
574c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::fill_n(columns.begin() + 500, 100, true);
575c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
576c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SkBitmap source =
577c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
578c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SkBitmap result = ComputeDecimatedImage(source, rows, columns);
579c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_FALSE(result.empty());
580c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(300, result.width());
581c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ(200, result.height());
582c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
583c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // The call should have removed all empty spaces.
584c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ASSERT_TRUE(CompareImageFragments(source,
585c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                    result,
586c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                    gfx::Size(100, 100),
587c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                    gfx::Point(100, 200),
588c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                    gfx::Point(0, 0)));
589c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ASSERT_TRUE(CompareImageFragments(source,
590c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                    result,
591c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                    gfx::Size(100, 100),
592c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                    gfx::Point(300, 200),
593c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                    gfx::Point(100, 0)));
594c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ASSERT_TRUE(CompareImageFragments(source,
595c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                    result,
596c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                    gfx::Size(100, 100),
597c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                    gfx::Point(500, 200),
598c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                    gfx::Point(200, 0)));
599c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ASSERT_TRUE(CompareImageFragments(source,
600c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                    result,
601c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                    gfx::Size(100, 100),
602c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                    gfx::Point(100, 400),
6034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                    gfx::Point(0, 100)));
604c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
605c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
606868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)TEST_F(ThumbnailContentAnalysisTest, CreateRetargetedThumbnailImage) {
607c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::Size image_size(1200, 1300);
60868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  gfx::Canvas canvas(image_size, 1.0f, true);
609c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
610c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // The following will create a 'fake image' consisting of color blocks placed
611c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // on a neutral background. The entire layout is supposed to mimic a
612c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // screenshot of a web page.
613c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // The tested function is supposed to locate the interesing areas in the
614c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // middle.
615c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const int margin_horizontal = 60;
616c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const int margin_vertical = 20;
617c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  canvas.FillRect(gfx::Rect(image_size), SkColorSetRGB(200, 210, 210));
618c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const gfx::Rect header_rect(margin_horizontal,
619c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                              margin_vertical,
620c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                              image_size.width() - 2 * margin_horizontal,
621c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                              100);
622c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const gfx::Rect footer_rect(margin_horizontal,
623c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                              image_size.height() - margin_vertical - 100,
624c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                              image_size.width() - 2 * margin_horizontal,
625c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                              100);
626c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const gfx::Rect body_rect(margin_horizontal,
627c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                            header_rect.bottom() + margin_vertical,
628c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                            image_size.width() - 2 * margin_horizontal,
629c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                            footer_rect.y() - header_rect.bottom() -
630c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                            2 * margin_vertical);
631c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  canvas.FillRect(header_rect, SkColorSetRGB(200, 40, 10));
632c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  canvas.FillRect(footer_rect, SkColorSetRGB(10, 40, 180));
633c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  canvas.FillRect(body_rect, SkColorSetRGB(150, 180, 40));
634c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
635c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // 'Fine print' at the bottom.
636c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const int fine_print = 8;
637c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const SkColor print_color = SkColorSetRGB(45, 30, 30);
638c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (int y = footer_rect.y() + fine_print;
639c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)       y < footer_rect.bottom() - fine_print;
640c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)       y += 2 * fine_print) {
641c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for (int x = footer_rect.x() + fine_print;
642c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)         x < footer_rect.right() - fine_print;
643c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)         x += 2 * fine_print) {
644c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      canvas.DrawRect(gfx::Rect(x, y, fine_print, fine_print),  print_color);
645c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
646c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
647c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
648c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Blocky content at the top.
649c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const int block_size = header_rect.height() - margin_vertical;
650c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (int x = header_rect.x() + margin_horizontal;
651c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)       x < header_rect.right() - block_size;
652c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)       x += block_size + margin_horizontal) {
653c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const int half_block = block_size / 2 - 5;
654c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const SkColor block_color = SkColorSetRGB(255, 255, 255);
655c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const int y = header_rect.y() + margin_vertical / 2;
656c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    int second_col = x + half_block + 10;
657c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    int second_row = y + half_block + 10;
658c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    canvas.FillRect(gfx::Rect(x, y, half_block, block_size), block_color);
659c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    canvas.FillRect(gfx::Rect(second_col,  y, half_block, half_block),
660c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                    block_color);
661c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    canvas.FillRect(gfx::Rect(second_col, second_row, half_block, half_block),
662c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                    block_color);
663c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
664c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
665c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Now the main body. Mostly text with some 'pictures'.
666c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (int y = body_rect.y() + fine_print;
667c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)       y < body_rect.bottom() - fine_print;
668c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)       y += 2 * fine_print) {
669c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for (int x = body_rect.x() + fine_print;
670c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)         x < body_rect.right() - fine_print;
671c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)         x += 2 * fine_print) {
672c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      canvas.DrawRect(gfx::Rect(x, y, fine_print, fine_print),  print_color);
673c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
674c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
675c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
676c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (int line = 0; line < 3; ++line) {
677c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    int alignment = line % 2;
678c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const int y = body_rect.y() +
679c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        body_rect.height() / 3 * line + margin_vertical;
680c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const int x = body_rect.x() +
681c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        alignment * body_rect.width() / 2 + margin_vertical;
682c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    gfx::Rect pict_rect(x, y,
683c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                        body_rect.width() / 2 - 2 * margin_vertical,
684c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                        body_rect.height() / 3 - 2 * margin_vertical);
685c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    canvas.FillRect(pict_rect, SkColorSetRGB(255, 255, 255));
686c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    canvas.DrawRect(pict_rect, SkColorSetRGB(0, 0, 0));
687c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
688c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
689c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SkBitmap source =
690c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
691c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
692868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  SkBitmap result = CreateRetargetedThumbnailImage(
693c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      source, gfx::Size(424, 264), 2.5);
694c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_FALSE(result.empty());
695c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
696c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Given the nature of computation We can't really assert much here about the
697c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // image itself. We know it should have been computed, should be smaller than
698c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // the original and it must not be zero.
699c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_LT(result.width(), image_size.width());
700c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_LT(result.height(), image_size.height());
701c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
702c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int histogram[256] = {};
703c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  color_utils::BuildLumaHistogram(result, histogram);
704c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int non_zero_color_count = std::count_if(
705c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      histogram, histogram + 256, std::bind2nd(std::greater<int>(), 0));
706c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_GT(non_zero_color_count, 4);
707c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
708c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
709c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
710c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}  // namespace thumbnailing_utils
711