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 5 #include "components/favicon_base/favicon_util.h" 6 7 #include <cmath> 8 9 #include "components/favicon_base/favicon_types.h" 10 #include "components/favicon_base/select_favicon_frames.h" 11 #include "skia/ext/image_operations.h" 12 #include "third_party/skia/include/core/SkBitmap.h" 13 #include "third_party/skia/include/core/SkCanvas.h" 14 #include "ui/base/layout.h" 15 #include "ui/gfx/codec/png_codec.h" 16 #include "ui/gfx/favicon_size.h" 17 #include "ui/gfx/image/image_png_rep.h" 18 #include "ui/gfx/image/image_skia.h" 19 #include "ui/gfx/size.h" 20 21 #if defined(OS_MACOSX) && !defined(OS_IOS) 22 #include "base/mac/mac_util.h" 23 #endif // defined(OS_MACOSX) && !defined(OS_IOS) 24 25 namespace favicon_base { 26 namespace { 27 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 std::vector<gfx::ImagePNGRep> png_reps; 36 if (png_data.empty()) 37 return png_reps; 38 39 // A |favicon_size| of 0 indicates that the largest frame is desired. 40 if (favicon_size == 0) { 41 int maximum_area = 0; 42 scoped_refptr<base::RefCountedMemory> best_candidate; 43 for (size_t i = 0; i < png_data.size(); ++i) { 44 int area = png_data[i].pixel_size.GetArea(); 45 if (area > maximum_area) { 46 maximum_area = area; 47 best_candidate = png_data[i].bitmap_data; 48 } 49 } 50 png_reps.push_back(gfx::ImagePNGRep(best_candidate, 1.0f)); 51 return png_reps; 52 } 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 = std::ceil(favicon_size * favicon_scales[i]); 59 desired_pixel_sizes[pixel_size] = favicon_scales[i]; 60 } 61 62 for (size_t i = 0; i < png_data.size(); ++i) { 63 if (!png_data[i].is_valid()) 64 continue; 65 66 const gfx::Size& pixel_size = png_data[i].pixel_size; 67 if (pixel_size.width() != pixel_size.height()) 68 continue; 69 70 std::map<int, float>::iterator it = 71 desired_pixel_sizes.find(pixel_size.width()); 72 if (it == desired_pixel_sizes.end()) 73 continue; 74 75 png_reps.push_back(gfx::ImagePNGRep(png_data[i].bitmap_data, 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 std::vector<float> GetFaviconScales() { 140 const float kScale1x = 1.0f; 141 std::vector<ui::ScaleFactor> resource_scale_factors = 142 ui::GetSupportedScaleFactors(); 143 144 // TODO(ios): 1.0f should not be necessary on iOS retina devices. However 145 // the sync service only supports syncing 100p favicons. Until sync supports 146 // other scales 100p is needed in the list of scales to retrieve and 147 // store the favicons in both 100p for sync and 200p for display. cr/160503. 148 std::vector<float> favicon_scales(1, kScale1x); 149 for (size_t i = 0; i < resource_scale_factors.size(); ++i) { 150 if (resource_scale_factors[i] != ui::SCALE_FACTOR_100P) 151 favicon_scales.push_back( 152 ui::GetScaleForScaleFactor(resource_scale_factors[i])); 153 } 154 return favicon_scales; 155 } 156 157 void SetFaviconColorSpace(gfx::Image* image) { 158 #if defined(OS_MACOSX) && !defined(OS_IOS) 159 image->SetSourceColorSpace(base::mac::GetSystemColorSpace()); 160 #endif // defined(OS_MACOSX) && !defined(OS_IOS) 161 } 162 163 gfx::Image SelectFaviconFramesFromPNGs( 164 const std::vector<favicon_base::FaviconRawBitmapResult>& png_data, 165 const std::vector<float>& favicon_scales, 166 int favicon_size) { 167 // Create image reps for as many scales as possible without resizing 168 // the bitmap data or decoding it. FaviconHandler stores already resized 169 // favicons into history so no additional resizing should be needed in the 170 // common case. 171 // Creating the gfx::Image from |png_data| without resizing or decoding if 172 // possible is important because: 173 // - Sync does a byte-to-byte comparison of gfx::Image::As1xPNGBytes() to 174 // the data it put into the database in order to determine whether any 175 // updates should be pushed to sync. 176 // - The decoding occurs on the UI thread and the decoding can be a 177 // significant performance hit if a user has many bookmarks. 178 // TODO(pkotwicz): Move the decoding off the UI thread. 179 std::vector<gfx::ImagePNGRep> png_reps = 180 SelectFaviconFramesFromPNGsWithoutResizing( 181 png_data, favicon_scales, favicon_size); 182 183 // SelectFaviconFramesFromPNGsWithoutResizing() should have selected the 184 // largest favicon if |favicon_size| == 0. 185 if (favicon_size == 0) 186 return gfx::Image(png_reps); 187 188 std::vector<float> favicon_scales_to_generate = favicon_scales; 189 for (size_t i = 0; i < png_reps.size(); ++i) { 190 for (int j = static_cast<int>(favicon_scales_to_generate.size()) - 1; 191 j >= 0; 192 --j) { 193 if (png_reps[i].scale == favicon_scales_to_generate[j]) { 194 favicon_scales_to_generate.erase(favicon_scales_to_generate.begin() + 195 j); 196 } 197 } 198 } 199 200 if (favicon_scales_to_generate.empty()) 201 return gfx::Image(png_reps); 202 203 std::vector<SkBitmap> bitmaps; 204 for (size_t i = 0; i < png_data.size(); ++i) { 205 if (!png_data[i].is_valid()) 206 continue; 207 208 SkBitmap bitmap; 209 if (gfx::PNGCodec::Decode(png_data[i].bitmap_data->front(), 210 png_data[i].bitmap_data->size(), 211 &bitmap)) { 212 bitmaps.push_back(bitmap); 213 } 214 } 215 216 if (bitmaps.empty()) 217 return gfx::Image(); 218 219 gfx::ImageSkia resized_image_skia; 220 for (size_t i = 0; i < favicon_scales_to_generate.size(); ++i) { 221 float scale = favicon_scales_to_generate[i]; 222 int desired_size_in_pixel = std::ceil(favicon_size * scale); 223 SkBitmap bitmap = 224 ResizeBitmapByDownsamplingIfPossible(bitmaps, desired_size_in_pixel); 225 resized_image_skia.AddRepresentation(gfx::ImageSkiaRep(bitmap, scale)); 226 } 227 228 if (png_reps.empty()) 229 return gfx::Image(resized_image_skia); 230 231 std::vector<gfx::ImageSkiaRep> resized_image_skia_reps = 232 resized_image_skia.image_reps(); 233 for (size_t i = 0; i < resized_image_skia_reps.size(); ++i) { 234 scoped_refptr<base::RefCountedBytes> png_bytes(new base::RefCountedBytes()); 235 if (gfx::PNGCodec::EncodeBGRASkBitmap( 236 resized_image_skia_reps[i].sk_bitmap(), 237 false, 238 &png_bytes->data())) { 239 png_reps.push_back( 240 gfx::ImagePNGRep(png_bytes, resized_image_skia_reps[i].scale())); 241 } 242 } 243 244 return gfx::Image(png_reps); 245 } 246 247 } // namespace favicon_base 248