OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/common/thumbnail_score.h" | |
6 | |
7 #include "base/logging.h" | |
8 #include "base/strings/stringprintf.h" | |
9 | |
10 using base::Time; | |
11 using base::TimeDelta; | |
12 | |
13 const int64 ThumbnailScore::kUpdateThumbnailTimeDays = 1; | |
14 const double ThumbnailScore::kThumbnailMaximumBoringness = 0.94; | |
15 const double ThumbnailScore::kThumbnailDegradePerHour = 0.01; | |
16 const double ThumbnailScore::kTooWideAspectRatio = 2.0; | |
17 | |
18 // Calculates a numeric score from traits about where a snapshot was | |
19 // taken. The lower the better. We store the raw components in the | |
20 // database because I'm sure this will evolve and I don't want to break | |
21 // databases. | |
22 static int GetThumbnailType(const ThumbnailScore& score) { | |
23 int type = 0; | |
24 if (!score.at_top) | |
25 type += 1; | |
26 if (!score.good_clipping) | |
27 type += 2; | |
28 if (!score.load_completed) | |
29 type += 3; | |
30 return type; | |
31 } | |
32 | |
33 ThumbnailScore::ThumbnailScore() | |
34 : boring_score(1.0), | |
35 good_clipping(false), | |
36 at_top(false), | |
37 load_completed(false), | |
38 time_at_snapshot(Time::Now()), | |
39 redirect_hops_from_dest(0) { | |
40 } | |
41 | |
42 ThumbnailScore::ThumbnailScore(double score, bool clipping, bool top) | |
43 : boring_score(score), | |
44 good_clipping(clipping), | |
45 at_top(top), | |
46 load_completed(false), | |
47 time_at_snapshot(Time::Now()), | |
48 redirect_hops_from_dest(0) { | |
49 } | |
50 | |
51 ThumbnailScore::ThumbnailScore(double score, bool clipping, bool top, | |
52 const Time& time) | |
53 : boring_score(score), | |
54 good_clipping(clipping), | |
55 at_top(top), | |
56 load_completed(false), | |
57 time_at_snapshot(time), | |
58 redirect_hops_from_dest(0) { | |
59 } | |
60 | |
61 ThumbnailScore::~ThumbnailScore() { | |
62 } | |
63 | |
64 bool ThumbnailScore::Equals(const ThumbnailScore& rhs) const { | |
65 return boring_score == rhs.boring_score && | |
66 good_clipping == rhs.good_clipping && | |
67 at_top == rhs.at_top && | |
68 time_at_snapshot == rhs.time_at_snapshot && | |
69 redirect_hops_from_dest == rhs.redirect_hops_from_dest; | |
70 } | |
71 | |
72 std::string ThumbnailScore::ToString() const { | |
73 return base::StringPrintf( | |
74 "boring_score: %f, at_top %d, good_clipping %d, " | |
75 "load_completed: %d, " | |
76 "time_at_snapshot: %f, redirect_hops_from_dest: %d", | |
77 boring_score, | |
78 at_top, | |
79 good_clipping, | |
80 load_completed, | |
81 time_at_snapshot.ToDoubleT(), | |
82 redirect_hops_from_dest); | |
83 } | |
84 | |
85 bool ShouldReplaceThumbnailWith(const ThumbnailScore& current, | |
86 const ThumbnailScore& replacement) { | |
87 int current_type = GetThumbnailType(current); | |
88 int replacement_type = GetThumbnailType(replacement); | |
89 if (replacement_type < current_type) { | |
90 // If we have a better class of thumbnail, add it if it meets | |
91 // certain minimum boringness. | |
92 return replacement.boring_score < | |
93 ThumbnailScore::kThumbnailMaximumBoringness; | |
94 } else if (replacement_type == current_type) { | |
95 // It's much easier to do the scaling below when we're dealing with "higher | |
96 // is better." Then we can decrease the score by dividing by a fraction. | |
97 const double kThumbnailMinimumInterestingness = | |
98 1.0 - ThumbnailScore::kThumbnailMaximumBoringness; | |
99 double current_interesting_score = 1.0 - current.boring_score; | |
100 double replacement_interesting_score = 1.0 - replacement.boring_score; | |
101 | |
102 // Degrade the score of each thumbnail to account for how many redirects | |
103 // they are away from the destination. 1/(x+1) gives a scaling factor of | |
104 // one for x = 0, and asymptotically approaches 0 for larger values of x. | |
105 current_interesting_score *= | |
106 1.0 / (current.redirect_hops_from_dest + 1); | |
107 replacement_interesting_score *= | |
108 1.0 / (replacement.redirect_hops_from_dest + 1); | |
109 | |
110 // Degrade the score and prefer the newer one based on how long apart the | |
111 // two thumbnails were taken. This means we'll eventually replace an old | |
112 // good one with a new worse one assuming enough time has passed. | |
113 TimeDelta time_between_thumbnails = | |
114 replacement.time_at_snapshot - current.time_at_snapshot; | |
115 current_interesting_score -= time_between_thumbnails.InHours() * | |
116 ThumbnailScore::kThumbnailDegradePerHour; | |
117 | |
118 if (current_interesting_score < kThumbnailMinimumInterestingness) | |
119 current_interesting_score = kThumbnailMinimumInterestingness; | |
120 if (replacement_interesting_score > current_interesting_score) | |
121 return true; | |
122 } | |
123 | |
124 // If the current thumbnail doesn't meet basic boringness | |
125 // requirements, but the replacement does, always replace the | |
126 // current one even if we're using a worse thumbnail type. | |
127 return current.boring_score >= ThumbnailScore::kThumbnailMaximumBoringness && | |
128 replacement.boring_score < ThumbnailScore::kThumbnailMaximumBoringness; | |
129 } | |
130 | |
131 bool ThumbnailScore::ShouldConsiderUpdating() { | |
132 const TimeDelta time_elapsed = Time::Now() - time_at_snapshot; | |
133 if (time_elapsed < TimeDelta::FromDays(kUpdateThumbnailTimeDays) && | |
134 good_clipping && at_top && load_completed) { | |
135 // The current thumbnail is new and has good properties. | |
136 return false; | |
137 } | |
138 // The current thumbnail should be updated. | |
139 return true; | |
140 } | |
OLD | NEW |