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