// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/favicon_base/favicon_util.h" #include #include #include #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "components/favicon_base/favicon_types.h" #include "components/favicon_base/select_favicon_frames.h" #include "skia/ext/image_operations.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkImage.h" #include "ui/base/layout.h" #include "ui/gfx/codec/png_codec.h" #include "ui/gfx/favicon_size.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/image/image_png_rep.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_skia_rep.h" #if BUILDFLAG(IS_MAC) #include "base/mac/mac_util.h" #endif // BUILDFLAG(IS_MAC) namespace favicon_base { namespace { // Creates image reps of DIP size |favicon_size| for the subset of // |favicon_scales| for which the image reps can be created without resizing // or decoding the bitmap data. std::vector SelectFaviconFramesFromPNGsWithoutResizing( const std::vector& png_data, const std::vector& favicon_scales, int favicon_size) { TRACE_EVENT0("browser", "FaviconUtil::SelectFaviconFramesFromPNGsWithoutResizing"); std::vector png_reps; if (png_data.empty()) return png_reps; // A |favicon_size| of 0 indicates that the largest frame is desired. if (favicon_size == 0) { int maximum_area = 0; scoped_refptr best_candidate; for (size_t i = 0; i < png_data.size(); ++i) { int area = png_data[i].pixel_size.GetArea(); if (area > maximum_area) { maximum_area = area; best_candidate = png_data[i].bitmap_data; } } png_reps.push_back(gfx::ImagePNGRep(best_candidate, 1.0f)); return png_reps; } // Build a map which will be used to determine the scale used to // create a bitmap with given pixel size. std::map desired_pixel_sizes; for (size_t i = 0; i < favicon_scales.size(); ++i) { int pixel_size = static_cast(std::ceil(favicon_size * favicon_scales[i])); desired_pixel_sizes[pixel_size] = favicon_scales[i]; } for (size_t i = 0; i < png_data.size(); ++i) { if (!png_data[i].is_valid()) continue; const gfx::Size& pixel_size = png_data[i].pixel_size; if (pixel_size.width() != pixel_size.height()) continue; auto it = desired_pixel_sizes.find(pixel_size.width()); if (it == desired_pixel_sizes.end()) continue; png_reps.push_back(gfx::ImagePNGRep(png_data[i].bitmap_data, it->second)); } return png_reps; } // Returns a resampled bitmap of |desired_size| x |desired_size| by resampling // the best bitmap out of |input_bitmaps|. // ResizeBitmapByDownsamplingIfPossible() is similar to SelectFaviconFrames() // but it operates on bitmaps which have already been resampled via // SelectFaviconFrames(). SkBitmap ResizeBitmapByDownsamplingIfPossible( const std::vector& input_bitmaps, int desired_size) { DCHECK(!input_bitmaps.empty()); DCHECK_NE(0, desired_size); SkBitmap best_bitmap; for (size_t i = 0; i < input_bitmaps.size(); ++i) { const SkBitmap& input_bitmap = input_bitmaps[i]; if (input_bitmap.width() == desired_size && input_bitmap.height() == desired_size) { return input_bitmap; } else if (best_bitmap.isNull()) { best_bitmap = input_bitmap; } else if (input_bitmap.width() >= best_bitmap.width() && input_bitmap.height() >= best_bitmap.height()) { if (best_bitmap.width() < desired_size || best_bitmap.height() < desired_size) { best_bitmap = input_bitmap; } } else { if (input_bitmap.width() >= desired_size && input_bitmap.height() >= desired_size) { best_bitmap = input_bitmap; } } } if (desired_size % best_bitmap.width() == 0 && desired_size % best_bitmap.height() == 0) { // Use nearest neighbour resampling if upsampling by an integer. This // makes the result look similar to the result of SelectFaviconFrames(). SkBitmap bitmap; bitmap.allocN32Pixels(desired_size, desired_size); if (!best_bitmap.isOpaque()) bitmap.eraseARGB(0, 0, 0, 0); SkCanvas canvas(bitmap, SkSurfaceProps{}); canvas.drawImageRect(best_bitmap.asImage(), SkRect::MakeIWH(desired_size, desired_size), SkSamplingOptions()); return bitmap; } return skia::ImageOperations::Resize(best_bitmap, skia::ImageOperations::RESIZE_LANCZOS3, desired_size, desired_size); } } // namespace std::vector GetFaviconScales() { const float kScale1x = 1.0f; std::vector resource_scale_factors = ui::GetSupportedResourceScaleFactors(); // TODO(ios): 1.0f should not be necessary on iOS retina devices. However // the sync service only supports syncing 100p favicons. Until sync supports // other scales 100p is needed in the list of scales to retrieve and // store the favicons in both 100p for sync and 200p for display. cr/160503. std::vector favicon_scales(1, kScale1x); for (size_t i = 0; i < resource_scale_factors.size(); ++i) { if (resource_scale_factors[i] != ui::k100Percent) favicon_scales.push_back( ui::GetScaleForResourceScaleFactor(resource_scale_factors[i])); } return favicon_scales; } void SetFaviconColorSpace(gfx::Image* image) { #if BUILDFLAG(IS_MAC) image->SetSourceColorSpace(base::mac::GetSystemColorSpace()); #endif // BUILDFLAG(IS_MAC) } gfx::Image SelectFaviconFramesFromPNGs( const std::vector& png_data, const std::vector& favicon_scales, int favicon_size) { TRACE_EVENT0("browser", "FaviconUtil::SelectFaviconFramesFromPNGs"); // Create image reps for as many scales as possible without resizing // the bitmap data or decoding it. FaviconHandler stores already resized // favicons into history so no additional resizing should be needed in the // common case. // Creating the gfx::Image from |png_data| without resizing or decoding if // possible is important because: // - Sync does a byte-to-byte comparison of gfx::Image::As1xPNGBytes() to // the data it put into the database in order to determine whether any // updates should be pushed to sync. // - The decoding occurs on the UI thread and the decoding can be a // significant performance hit if a user has many bookmarks. // TODO(pkotwicz): Move the decoding off the UI thread. std::vector png_reps = SelectFaviconFramesFromPNGsWithoutResizing( png_data, favicon_scales, favicon_size); // SelectFaviconFramesFromPNGsWithoutResizing() should have selected the // largest favicon if |favicon_size| == 0. if (favicon_size == 0) return gfx::Image(png_reps); std::vector favicon_scales_to_generate = favicon_scales; for (size_t i = 0; i < png_reps.size(); ++i) { auto iter = std::find(favicon_scales_to_generate.begin(), favicon_scales_to_generate.end(), png_reps[i].scale); if (iter != favicon_scales_to_generate.end()) favicon_scales_to_generate.erase(iter); } if (favicon_scales_to_generate.empty()) return gfx::Image(png_reps); std::vector bitmaps; for (size_t i = 0; i < png_data.size(); ++i) { if (!png_data[i].is_valid()) continue; SkBitmap bitmap; if (gfx::PNGCodec::Decode(png_data[i].bitmap_data->front(), png_data[i].bitmap_data->size(), &bitmap)) { bitmaps.push_back(bitmap); } } if (bitmaps.empty()) return gfx::Image(); gfx::ImageSkia resized_image_skia; for (size_t i = 0; i < favicon_scales_to_generate.size(); ++i) { float scale = favicon_scales_to_generate[i]; int desired_size_in_pixel = static_cast(std::ceil(favicon_size * scale)); SkBitmap bitmap = ResizeBitmapByDownsamplingIfPossible(bitmaps, desired_size_in_pixel); resized_image_skia.AddRepresentation(gfx::ImageSkiaRep(bitmap, scale)); } if (png_reps.empty()) return gfx::Image(resized_image_skia); std::vector resized_image_skia_reps = resized_image_skia.image_reps(); for (size_t i = 0; i < resized_image_skia_reps.size(); ++i) { scoped_refptr png_bytes(new base::RefCountedBytes()); if (gfx::PNGCodec::EncodeBGRASkBitmap( resized_image_skia_reps[i].GetBitmap(), false, &png_bytes->data())) { png_reps.push_back( gfx::ImagePNGRep(png_bytes, resized_image_skia_reps[i].scale())); } } return gfx::Image(png_reps); } favicon_base::FaviconRawBitmapResult ResizeFaviconBitmapResult( const std::vector& favicon_bitmap_results, int desired_size_in_pixel) { TRACE_EVENT0("browser", "FaviconUtil::ResizeFaviconBitmapResult"); if (favicon_bitmap_results.empty() || !favicon_bitmap_results[0].is_valid()) return favicon_base::FaviconRawBitmapResult(); favicon_base::FaviconRawBitmapResult bitmap_result = favicon_bitmap_results[0]; // If the desired size is 0, SelectFaviconFrames() will return the largest // bitmap without doing any resizing. As |favicon_bitmap_results| has bitmap // data for a single bitmap, return it and avoid an unnecessary decode. if (desired_size_in_pixel == 0) return bitmap_result; // If history bitmap is already desired pixel size, return early. if (bitmap_result.pixel_size.width() == desired_size_in_pixel && bitmap_result.pixel_size.height() == desired_size_in_pixel) return bitmap_result; // Convert raw bytes to SkBitmap, resize via SelectFaviconFrames(), then // convert back. std::vector desired_favicon_scales; desired_favicon_scales.push_back(1.0f); gfx::Image resized_image = favicon_base::SelectFaviconFramesFromPNGs( favicon_bitmap_results, desired_favicon_scales, desired_size_in_pixel); std::vector resized_bitmap_data; if (!gfx::PNGCodec::EncodeBGRASkBitmap(resized_image.AsBitmap(), false, &resized_bitmap_data)) { return favicon_base::FaviconRawBitmapResult(); } bitmap_result.bitmap_data = base::RefCountedBytes::TakeVector( &resized_bitmap_data); bitmap_result.pixel_size = gfx::Size(desired_size_in_pixel, desired_size_in_pixel); return bitmap_result; } } // namespace favicon_base