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. 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(). 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 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 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 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