favicon_util.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  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/favicon_util.h"
  5. #include <stddef.h>
  6. #include <algorithm>
  7. #include <cmath>
  8. #include "base/trace_event/trace_event.h"
  9. #include "build/build_config.h"
  10. #include "components/favicon_base/favicon_types.h"
  11. #include "components/favicon_base/select_favicon_frames.h"
  12. #include "skia/ext/image_operations.h"
  13. #include "third_party/skia/include/core/SkBitmap.h"
  14. #include "third_party/skia/include/core/SkCanvas.h"
  15. #include "third_party/skia/include/core/SkImage.h"
  16. #include "ui/base/layout.h"
  17. #include "ui/gfx/codec/png_codec.h"
  18. #include "ui/gfx/favicon_size.h"
  19. #include "ui/gfx/geometry/size.h"
  20. #include "ui/gfx/image/image_png_rep.h"
  21. #include "ui/gfx/image/image_skia.h"
  22. #include "ui/gfx/image/image_skia_rep.h"
  23. #if BUILDFLAG(IS_MAC)
  24. #include "base/mac/mac_util.h"
  25. #endif // BUILDFLAG(IS_MAC)
  26. namespace favicon_base {
  27. namespace {
  28. // Creates image reps of DIP size |favicon_size| for the subset of
  29. // |favicon_scales| for which the image reps can be created without resizing
  30. // or decoding the bitmap data.
  31. std::vector<gfx::ImagePNGRep> SelectFaviconFramesFromPNGsWithoutResizing(
  32. const std::vector<favicon_base::FaviconRawBitmapResult>& png_data,
  33. const std::vector<float>& favicon_scales,
  34. int favicon_size) {
  35. TRACE_EVENT0("browser",
  36. "FaviconUtil::SelectFaviconFramesFromPNGsWithoutResizing");
  37. std::vector<gfx::ImagePNGRep> png_reps;
  38. if (png_data.empty())
  39. return png_reps;
  40. // A |favicon_size| of 0 indicates that the largest frame is desired.
  41. if (favicon_size == 0) {
  42. int maximum_area = 0;
  43. scoped_refptr<base::RefCountedMemory> best_candidate;
  44. for (size_t i = 0; i < png_data.size(); ++i) {
  45. int area = png_data[i].pixel_size.GetArea();
  46. if (area > maximum_area) {
  47. maximum_area = area;
  48. best_candidate = png_data[i].bitmap_data;
  49. }
  50. }
  51. png_reps.push_back(gfx::ImagePNGRep(best_candidate, 1.0f));
  52. return png_reps;
  53. }
  54. // Build a map which will be used to determine the scale used to
  55. // create a bitmap with given pixel size.
  56. std::map<int, float> desired_pixel_sizes;
  57. for (size_t i = 0; i < favicon_scales.size(); ++i) {
  58. int pixel_size =
  59. static_cast<int>(std::ceil(favicon_size * favicon_scales[i]));
  60. desired_pixel_sizes[pixel_size] = favicon_scales[i];
  61. }
  62. for (size_t i = 0; i < png_data.size(); ++i) {
  63. if (!png_data[i].is_valid())
  64. continue;
  65. const gfx::Size& pixel_size = png_data[i].pixel_size;
  66. if (pixel_size.width() != pixel_size.height())
  67. continue;
  68. auto it = desired_pixel_sizes.find(pixel_size.width());
  69. if (it == desired_pixel_sizes.end())
  70. continue;
  71. png_reps.push_back(gfx::ImagePNGRep(png_data[i].bitmap_data, it->second));
  72. }
  73. return png_reps;
  74. }
  75. // Returns a resampled bitmap of |desired_size| x |desired_size| by resampling
  76. // the best bitmap out of |input_bitmaps|.
  77. // ResizeBitmapByDownsamplingIfPossible() is similar to SelectFaviconFrames()
  78. // but it operates on bitmaps which have already been resampled via
  79. // SelectFaviconFrames().
  80. SkBitmap ResizeBitmapByDownsamplingIfPossible(
  81. const std::vector<SkBitmap>& input_bitmaps,
  82. int desired_size) {
  83. DCHECK(!input_bitmaps.empty());
  84. DCHECK_NE(0, desired_size);
  85. SkBitmap best_bitmap;
  86. for (size_t i = 0; i < input_bitmaps.size(); ++i) {
  87. const SkBitmap& input_bitmap = input_bitmaps[i];
  88. if (input_bitmap.width() == desired_size &&
  89. input_bitmap.height() == desired_size) {
  90. return input_bitmap;
  91. } else if (best_bitmap.isNull()) {
  92. best_bitmap = input_bitmap;
  93. } else if (input_bitmap.width() >= best_bitmap.width() &&
  94. input_bitmap.height() >= best_bitmap.height()) {
  95. if (best_bitmap.width() < desired_size ||
  96. best_bitmap.height() < desired_size) {
  97. best_bitmap = input_bitmap;
  98. }
  99. } else {
  100. if (input_bitmap.width() >= desired_size &&
  101. input_bitmap.height() >= desired_size) {
  102. best_bitmap = input_bitmap;
  103. }
  104. }
  105. }
  106. if (desired_size % best_bitmap.width() == 0 &&
  107. desired_size % best_bitmap.height() == 0) {
  108. // Use nearest neighbour resampling if upsampling by an integer. This
  109. // makes the result look similar to the result of SelectFaviconFrames().
  110. SkBitmap bitmap;
  111. bitmap.allocN32Pixels(desired_size, desired_size);
  112. if (!best_bitmap.isOpaque())
  113. bitmap.eraseARGB(0, 0, 0, 0);
  114. SkCanvas canvas(bitmap, SkSurfaceProps{});
  115. canvas.drawImageRect(best_bitmap.asImage(),
  116. SkRect::MakeIWH(desired_size, desired_size),
  117. SkSamplingOptions());
  118. return bitmap;
  119. }
  120. return skia::ImageOperations::Resize(best_bitmap,
  121. skia::ImageOperations::RESIZE_LANCZOS3,
  122. desired_size,
  123. desired_size);
  124. }
  125. } // namespace
  126. std::vector<float> GetFaviconScales() {
  127. const float kScale1x = 1.0f;
  128. std::vector<ui::ResourceScaleFactor> resource_scale_factors =
  129. ui::GetSupportedResourceScaleFactors();
  130. // TODO(ios): 1.0f should not be necessary on iOS retina devices. However
  131. // the sync service only supports syncing 100p favicons. Until sync supports
  132. // other scales 100p is needed in the list of scales to retrieve and
  133. // store the favicons in both 100p for sync and 200p for display. cr/160503.
  134. std::vector<float> favicon_scales(1, kScale1x);
  135. for (size_t i = 0; i < resource_scale_factors.size(); ++i) {
  136. if (resource_scale_factors[i] != ui::k100Percent)
  137. favicon_scales.push_back(
  138. ui::GetScaleForResourceScaleFactor(resource_scale_factors[i]));
  139. }
  140. return favicon_scales;
  141. }
  142. void SetFaviconColorSpace(gfx::Image* image) {
  143. #if BUILDFLAG(IS_MAC)
  144. image->SetSourceColorSpace(base::mac::GetSystemColorSpace());
  145. #endif // BUILDFLAG(IS_MAC)
  146. }
  147. gfx::Image SelectFaviconFramesFromPNGs(
  148. const std::vector<favicon_base::FaviconRawBitmapResult>& png_data,
  149. const std::vector<float>& favicon_scales,
  150. int favicon_size) {
  151. TRACE_EVENT0("browser", "FaviconUtil::SelectFaviconFramesFromPNGs");
  152. // Create image reps for as many scales as possible without resizing
  153. // the bitmap data or decoding it. FaviconHandler stores already resized
  154. // favicons into history so no additional resizing should be needed in the
  155. // common case.
  156. // Creating the gfx::Image from |png_data| without resizing or decoding if
  157. // possible is important because:
  158. // - Sync does a byte-to-byte comparison of gfx::Image::As1xPNGBytes() to
  159. // the data it put into the database in order to determine whether any
  160. // updates should be pushed to sync.
  161. // - The decoding occurs on the UI thread and the decoding can be a
  162. // significant performance hit if a user has many bookmarks.
  163. // TODO(pkotwicz): Move the decoding off the UI thread.
  164. std::vector<gfx::ImagePNGRep> png_reps =
  165. SelectFaviconFramesFromPNGsWithoutResizing(
  166. png_data, favicon_scales, favicon_size);
  167. // SelectFaviconFramesFromPNGsWithoutResizing() should have selected the
  168. // largest favicon if |favicon_size| == 0.
  169. if (favicon_size == 0)
  170. return gfx::Image(png_reps);
  171. std::vector<float> favicon_scales_to_generate = favicon_scales;
  172. for (size_t i = 0; i < png_reps.size(); ++i) {
  173. auto iter = std::find(favicon_scales_to_generate.begin(),
  174. favicon_scales_to_generate.end(), png_reps[i].scale);
  175. if (iter != favicon_scales_to_generate.end())
  176. favicon_scales_to_generate.erase(iter);
  177. }
  178. if (favicon_scales_to_generate.empty())
  179. return gfx::Image(png_reps);
  180. std::vector<SkBitmap> bitmaps;
  181. for (size_t i = 0; i < png_data.size(); ++i) {
  182. if (!png_data[i].is_valid())
  183. continue;
  184. SkBitmap bitmap;
  185. if (gfx::PNGCodec::Decode(png_data[i].bitmap_data->front(),
  186. png_data[i].bitmap_data->size(),
  187. &bitmap)) {
  188. bitmaps.push_back(bitmap);
  189. }
  190. }
  191. if (bitmaps.empty())
  192. return gfx::Image();
  193. gfx::ImageSkia resized_image_skia;
  194. for (size_t i = 0; i < favicon_scales_to_generate.size(); ++i) {
  195. float scale = favicon_scales_to_generate[i];
  196. int desired_size_in_pixel =
  197. static_cast<int>(std::ceil(favicon_size * scale));
  198. SkBitmap bitmap =
  199. ResizeBitmapByDownsamplingIfPossible(bitmaps, desired_size_in_pixel);
  200. resized_image_skia.AddRepresentation(gfx::ImageSkiaRep(bitmap, scale));
  201. }
  202. if (png_reps.empty())
  203. return gfx::Image(resized_image_skia);
  204. std::vector<gfx::ImageSkiaRep> resized_image_skia_reps =
  205. resized_image_skia.image_reps();
  206. for (size_t i = 0; i < resized_image_skia_reps.size(); ++i) {
  207. scoped_refptr<base::RefCountedBytes> png_bytes(new base::RefCountedBytes());
  208. if (gfx::PNGCodec::EncodeBGRASkBitmap(
  209. resized_image_skia_reps[i].GetBitmap(), false,
  210. &png_bytes->data())) {
  211. png_reps.push_back(
  212. gfx::ImagePNGRep(png_bytes, resized_image_skia_reps[i].scale()));
  213. }
  214. }
  215. return gfx::Image(png_reps);
  216. }
  217. favicon_base::FaviconRawBitmapResult ResizeFaviconBitmapResult(
  218. const std::vector<favicon_base::FaviconRawBitmapResult>&
  219. favicon_bitmap_results,
  220. int desired_size_in_pixel) {
  221. TRACE_EVENT0("browser", "FaviconUtil::ResizeFaviconBitmapResult");
  222. if (favicon_bitmap_results.empty() || !favicon_bitmap_results[0].is_valid())
  223. return favicon_base::FaviconRawBitmapResult();
  224. favicon_base::FaviconRawBitmapResult bitmap_result =
  225. favicon_bitmap_results[0];
  226. // If the desired size is 0, SelectFaviconFrames() will return the largest
  227. // bitmap without doing any resizing. As |favicon_bitmap_results| has bitmap
  228. // data for a single bitmap, return it and avoid an unnecessary decode.
  229. if (desired_size_in_pixel == 0)
  230. return bitmap_result;
  231. // If history bitmap is already desired pixel size, return early.
  232. if (bitmap_result.pixel_size.width() == desired_size_in_pixel &&
  233. bitmap_result.pixel_size.height() == desired_size_in_pixel)
  234. return bitmap_result;
  235. // Convert raw bytes to SkBitmap, resize via SelectFaviconFrames(), then
  236. // convert back.
  237. std::vector<float> desired_favicon_scales;
  238. desired_favicon_scales.push_back(1.0f);
  239. gfx::Image resized_image = favicon_base::SelectFaviconFramesFromPNGs(
  240. favicon_bitmap_results, desired_favicon_scales, desired_size_in_pixel);
  241. std::vector<unsigned char> resized_bitmap_data;
  242. if (!gfx::PNGCodec::EncodeBGRASkBitmap(resized_image.AsBitmap(), false,
  243. &resized_bitmap_data)) {
  244. return favicon_base::FaviconRawBitmapResult();
  245. }
  246. bitmap_result.bitmap_data = base::RefCountedBytes::TakeVector(
  247. &resized_bitmap_data);
  248. bitmap_result.pixel_size =
  249. gfx::Size(desired_size_in_pixel, desired_size_in_pixel);
  250. return bitmap_result;
  251. }
  252. } // namespace favicon_base