select_favicon_frames.cc 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. // Copyright 2014 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. #include "components/favicon_base/select_favicon_frames.h"
  5. #include <algorithm>
  6. #include <cmath>
  7. #include <limits>
  8. #include <map>
  9. #include <memory>
  10. #include <set>
  11. #include <utility>
  12. #include "base/containers/contains.h"
  13. #include "components/favicon_base/favicon_util.h"
  14. #include "skia/ext/image_operations.h"
  15. #include "third_party/skia/include/core/SkCanvas.h"
  16. #include "third_party/skia/include/core/SkImage.h"
  17. #include "ui/gfx/geometry/size.h"
  18. #include "ui/gfx/image/image.h"
  19. #include "ui/gfx/image/image_skia.h"
  20. #include "ui/gfx/image/image_skia_rep.h"
  21. #include "ui/gfx/image/image_skia_source.h"
  22. namespace {
  23. size_t BiggestCandidate(const std::vector<gfx::Size>& candidate_sizes) {
  24. size_t max_index = 0;
  25. int max_area = candidate_sizes[0].GetArea();
  26. for (size_t i = 1; i < candidate_sizes.size(); ++i) {
  27. int area = candidate_sizes[i].GetArea();
  28. if (area > max_area) {
  29. max_area = area;
  30. max_index = i;
  31. }
  32. }
  33. return max_index;
  34. }
  35. SkBitmap SampleNearestNeighbor(const SkBitmap& contents, int desired_size) {
  36. SkBitmap bitmap;
  37. bitmap.allocN32Pixels(desired_size, desired_size);
  38. if (!contents.isOpaque())
  39. bitmap.eraseARGB(0, 0, 0, 0);
  40. {
  41. SkCanvas canvas(bitmap, SkSurfaceProps{});
  42. canvas.drawImageRect(contents.asImage(),
  43. SkRect::MakeIWH(desired_size, desired_size),
  44. SkSamplingOptions());
  45. }
  46. return bitmap;
  47. }
  48. size_t GetCandidateIndexWithBestScore(
  49. const std::vector<gfx::Size>& candidate_sizes,
  50. int desired_size,
  51. float* output_score) {
  52. DCHECK_NE(desired_size, 0);
  53. // Try to find an exact match.
  54. for (size_t i = 0; i < candidate_sizes.size(); ++i) {
  55. if (candidate_sizes[i].width() == desired_size &&
  56. candidate_sizes[i].height() == desired_size) {
  57. *output_score = 1;
  58. return i;
  59. }
  60. }
  61. // Huge favicon bitmaps often have a completely different visual style from
  62. // smaller favicon bitmaps. Avoid them.
  63. const int kHugeEdgeSize = desired_size * 8;
  64. // Order of preference:
  65. // 1) Bitmaps with width and height smaller than |kHugeEdgeSize|.
  66. // 2) Bitmaps which need to be scaled down instead of up.
  67. // 3) Bitmaps which do not need to be scaled as much.
  68. size_t candidate_index = std::numeric_limits<size_t>::max();
  69. float candidate_score = 0;
  70. for (size_t i = 0; i < candidate_sizes.size(); ++i) {
  71. float average_edge =
  72. (candidate_sizes[i].width() + candidate_sizes[i].height()) / 2.0f;
  73. float score = 0;
  74. if (candidate_sizes[i].width() >= kHugeEdgeSize ||
  75. candidate_sizes[i].height() >= kHugeEdgeSize) {
  76. score = std::min(1.0f, desired_size / average_edge) * 0.01f;
  77. } else if (candidate_sizes[i].width() >= desired_size &&
  78. candidate_sizes[i].height() >= desired_size) {
  79. score = desired_size / average_edge * 0.01f + 0.15f;
  80. } else {
  81. score = std::min(1.0f, average_edge / desired_size) * 0.01f + 0.1f;
  82. }
  83. if (candidate_index == std::numeric_limits<size_t>::max() ||
  84. score > candidate_score) {
  85. candidate_index = i;
  86. candidate_score = score;
  87. }
  88. }
  89. *output_score = candidate_score;
  90. return candidate_index;
  91. }
  92. // Represents the index of the best candidate for |desired_size| from the
  93. // |candidate_sizes| passed into GetCandidateIndicesWithBestScores().
  94. struct SelectionResult {
  95. // index in |candidate_sizes| of the best candidate.
  96. size_t index;
  97. // The desired size for which |index| is the best candidate.
  98. int desired_size;
  99. };
  100. void GetCandidateIndicesWithBestScores(
  101. const std::vector<gfx::Size>& candidate_sizes,
  102. const std::vector<int>& desired_sizes,
  103. float* match_score,
  104. std::vector<SelectionResult>* results) {
  105. if (candidate_sizes.empty() || desired_sizes.empty()) {
  106. if (match_score)
  107. *match_score = 0.0f;
  108. return;
  109. }
  110. if (base::Contains(desired_sizes, 0)) {
  111. // Just return the biggest image available.
  112. SelectionResult result;
  113. result.index = BiggestCandidate(candidate_sizes);
  114. result.desired_size = 0;
  115. results->push_back(result);
  116. if (match_score)
  117. *match_score = 1.0f;
  118. return;
  119. }
  120. float total_score = 0;
  121. for (size_t i = 0; i < desired_sizes.size(); ++i) {
  122. float score;
  123. SelectionResult result;
  124. result.desired_size = desired_sizes[i];
  125. result.index = GetCandidateIndexWithBestScore(
  126. candidate_sizes, result.desired_size, &score);
  127. results->push_back(result);
  128. total_score += score;
  129. }
  130. if (match_score)
  131. *match_score = total_score / desired_sizes.size();
  132. }
  133. // Resize |source_bitmap|
  134. SkBitmap GetResizedBitmap(const SkBitmap& source_bitmap,
  135. gfx::Size original_size,
  136. int desired_size_in_pixel) {
  137. if (desired_size_in_pixel == 0 ||
  138. (original_size.width() == desired_size_in_pixel &&
  139. original_size.height() == desired_size_in_pixel)) {
  140. return source_bitmap;
  141. }
  142. if (desired_size_in_pixel % original_size.width() == 0 &&
  143. desired_size_in_pixel % original_size.height() == 0) {
  144. return SampleNearestNeighbor(source_bitmap, desired_size_in_pixel);
  145. }
  146. return skia::ImageOperations::Resize(source_bitmap,
  147. skia::ImageOperations::RESIZE_LANCZOS3,
  148. desired_size_in_pixel,
  149. desired_size_in_pixel);
  150. }
  151. class FaviconImageSource : public gfx::ImageSkiaSource {
  152. public:
  153. FaviconImageSource() {}
  154. FaviconImageSource(const FaviconImageSource&) = delete;
  155. FaviconImageSource& operator=(const FaviconImageSource&) = delete;
  156. ~FaviconImageSource() override {}
  157. // gfx::ImageSkiaSource:
  158. gfx::ImageSkiaRep GetImageForScale(float scale) override {
  159. const gfx::ImageSkiaRep* rep = nullptr;
  160. // gfx::ImageSkia passes one of the resource scale factors. The source
  161. // should return:
  162. // 1) The ImageSkiaRep with the highest scale if all available
  163. // scales are smaller than |scale|.
  164. // 2) The ImageSkiaRep with the smallest one that is larger than |scale|.
  165. // Note: Keep this logic consistent with the PNGImageSource in
  166. // ui/gfx/image.cc.
  167. // TODO(oshima): consolidate these logic into one place.
  168. for (std::vector<gfx::ImageSkiaRep>::const_iterator iter =
  169. image_skia_reps_.begin();
  170. iter != image_skia_reps_.end(); ++iter) {
  171. if ((*iter).scale() == scale)
  172. return (*iter);
  173. if (!rep || rep->scale() < (*iter).scale())
  174. rep = &(*iter);
  175. if (rep->scale() >= scale)
  176. break;
  177. }
  178. DCHECK(rep);
  179. return rep ? *rep : gfx::ImageSkiaRep();
  180. }
  181. void AddImageSkiaRep(const gfx::ImageSkiaRep& rep) {
  182. image_skia_reps_.push_back(rep);
  183. }
  184. private:
  185. std::vector<gfx::ImageSkiaRep> image_skia_reps_;
  186. };
  187. } // namespace
  188. const float kSelectFaviconFramesInvalidScore = -1.0f;
  189. gfx::ImageSkia CreateFaviconImageSkia(
  190. const std::vector<SkBitmap>& bitmaps,
  191. const std::vector<gfx::Size>& original_sizes,
  192. int desired_size_in_dip,
  193. float* score) {
  194. DCHECK_EQ(bitmaps.size(), original_sizes.size());
  195. const std::vector<float>& favicon_scales = favicon_base::GetFaviconScales();
  196. std::vector<int> desired_sizes;
  197. if (desired_size_in_dip == 0) {
  198. desired_sizes.push_back(0);
  199. } else {
  200. for (auto iter = favicon_scales.begin(); iter != favicon_scales.end();
  201. ++iter) {
  202. desired_sizes.push_back(
  203. static_cast<int>(ceil(desired_size_in_dip * (*iter))));
  204. }
  205. }
  206. std::vector<SelectionResult> results;
  207. GetCandidateIndicesWithBestScores(original_sizes,
  208. desired_sizes,
  209. score,
  210. &results);
  211. if (results.size() == 0)
  212. return gfx::ImageSkia();
  213. if (desired_size_in_dip == 0) {
  214. size_t index = results[0].index;
  215. return gfx::ImageSkia::CreateFromBitmap(bitmaps[index], 1.0f);
  216. }
  217. auto image_source = std::make_unique<FaviconImageSource>();
  218. for (size_t i = 0; i < results.size(); ++i) {
  219. size_t index = results[i].index;
  220. image_source->AddImageSkiaRep(
  221. gfx::ImageSkiaRep(GetResizedBitmap(bitmaps[index],
  222. original_sizes[index],
  223. desired_sizes[i]),
  224. favicon_scales[i]));
  225. }
  226. return gfx::ImageSkia(std::move(image_source),
  227. gfx::Size(desired_size_in_dip, desired_size_in_dip));
  228. }
  229. void SelectFaviconFrameIndices(const std::vector<gfx::Size>& frame_pixel_sizes,
  230. const std::vector<int>& desired_sizes,
  231. std::vector<size_t>* best_indices,
  232. float* match_score) {
  233. std::vector<SelectionResult> results;
  234. GetCandidateIndicesWithBestScores(
  235. frame_pixel_sizes, desired_sizes, match_score, &results);
  236. if (!best_indices)
  237. return;
  238. std::set<size_t> already_added;
  239. for (size_t i = 0; i < results.size(); ++i) {
  240. size_t index = results[i].index;
  241. // GetCandidateIndicesWithBestScores() will return duplicate indices if the
  242. // bitmap data with |frame_pixel_sizes[index]| should be used for multiple
  243. // scale factors. Remove duplicates here such that |best_indices| contains
  244. // no duplicates.
  245. if (already_added.find(index) == already_added.end()) {
  246. already_added.insert(index);
  247. best_indices->push_back(index);
  248. }
  249. }
  250. }