| OLD | NEW |
| (Empty) |
| 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/select_favicon_frames.h" | |
| 6 | |
| 7 #include "skia/ext/image_operations.h" | |
| 8 #include "third_party/skia/include/core/SkCanvas.h" | |
| 9 #include "ui/gfx/image/image.h" | |
| 10 #include "ui/gfx/image/image_skia.h" | |
| 11 #include "ui/gfx/size.h" | |
| 12 | |
| 13 namespace { | |
| 14 | |
| 15 void SizesFromBitmaps(const std::vector<SkBitmap>& bitmaps, | |
| 16 std::vector<gfx::Size>* sizes) { | |
| 17 for (size_t i = 0; i < bitmaps.size(); ++i) | |
| 18 sizes->push_back(gfx::Size(bitmaps[i].width(), bitmaps[i].height())); | |
| 19 } | |
| 20 | |
| 21 size_t BiggestCandidate(const std::vector<gfx::Size>& candidate_sizes) { | |
| 22 size_t max_index = 0; | |
| 23 int max_area = candidate_sizes[0].GetArea(); | |
| 24 for (size_t i = 1; i < candidate_sizes.size(); ++i) { | |
| 25 int area = candidate_sizes[i].GetArea(); | |
| 26 if (area > max_area) { | |
| 27 max_area = area; | |
| 28 max_index = i; | |
| 29 } | |
| 30 } | |
| 31 return max_index; | |
| 32 } | |
| 33 | |
| 34 SkBitmap PadWithBorder(const SkBitmap& contents, | |
| 35 int desired_size, | |
| 36 int source_size) { | |
| 37 SkBitmap bitmap; | |
| 38 bitmap.setConfig( | |
| 39 SkBitmap::kARGB_8888_Config, desired_size, desired_size); | |
| 40 bitmap.allocPixels(); | |
| 41 bitmap.eraseARGB(0, 0, 0, 0); | |
| 42 | |
| 43 { | |
| 44 SkCanvas canvas(bitmap); | |
| 45 int shift = (desired_size - source_size) / 2; | |
| 46 SkRect dest(SkRect::MakeXYWH(shift, shift, source_size, source_size)); | |
| 47 canvas.drawBitmapRect(contents, NULL, dest); | |
| 48 } | |
| 49 | |
| 50 return bitmap; | |
| 51 } | |
| 52 | |
| 53 SkBitmap SampleNearestNeighbor(const SkBitmap& contents, int desired_size) { | |
| 54 SkBitmap bitmap; | |
| 55 bitmap.setConfig( | |
| 56 SkBitmap::kARGB_8888_Config, desired_size, desired_size); | |
| 57 bitmap.allocPixels(); | |
| 58 if (!contents.isOpaque()) | |
| 59 bitmap.eraseARGB(0, 0, 0, 0); | |
| 60 | |
| 61 { | |
| 62 SkCanvas canvas(bitmap); | |
| 63 SkRect dest(SkRect::MakeWH(desired_size, desired_size)); | |
| 64 canvas.drawBitmapRect(contents, NULL, dest); | |
| 65 } | |
| 66 | |
| 67 return bitmap; | |
| 68 } | |
| 69 | |
| 70 enum ResizeMethod { | |
| 71 NONE, | |
| 72 PAD_WITH_BORDER, | |
| 73 SAMPLE_NEAREST_NEIGHBOUR, | |
| 74 LANCZOS | |
| 75 }; | |
| 76 | |
| 77 size_t GetCandidateIndexWithBestScore( | |
| 78 const std::vector<gfx::Size>& candidate_sizes, | |
| 79 ui::ScaleFactor scale_factor, | |
| 80 int desired_size, | |
| 81 float* score, | |
| 82 ResizeMethod* resize_method) { | |
| 83 float scale = ui::GetScaleFactorScale(scale_factor); | |
| 84 desired_size = static_cast<int>(desired_size * scale + 0.5f); | |
| 85 | |
| 86 // Try to find an exact match. | |
| 87 for (size_t i = 0; i < candidate_sizes.size(); ++i) { | |
| 88 if (candidate_sizes[i].width() == desired_size && | |
| 89 candidate_sizes[i].height() == desired_size) { | |
| 90 *score = 1; | |
| 91 *resize_method = NONE; | |
| 92 return i; | |
| 93 } | |
| 94 } | |
| 95 | |
| 96 // If that failed, the following special rules apply: | |
| 97 // 1. 17px-24px images are built from 16px images by adding | |
| 98 // a transparent border. | |
| 99 if (desired_size > 16 * scale && desired_size <= 24 * scale) { | |
| 100 int source_size = static_cast<int>(16 * scale + 0.5f); | |
| 101 for (size_t i = 0; i < candidate_sizes.size(); ++i) { | |
| 102 if (candidate_sizes[i].width() == source_size && | |
| 103 candidate_sizes[i].height() == source_size) { | |
| 104 *score = 0.2f; | |
| 105 *resize_method = PAD_WITH_BORDER; | |
| 106 return i; | |
| 107 } | |
| 108 } | |
| 109 // Try again, with upsizing the base variant. | |
| 110 for (size_t i = 0; i < candidate_sizes.size(); ++i) { | |
| 111 if (candidate_sizes[i].width() * scale == source_size && | |
| 112 candidate_sizes[i].height() * scale == source_size) { | |
| 113 *score = 0.15f; | |
| 114 *resize_method = PAD_WITH_BORDER; | |
| 115 return i; | |
| 116 } | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 // 2. Integer multiples are built using nearest neighbor sampling. | |
| 121 // 3. Else, use Lancosz scaling: | |
| 122 // b) If available, from the next bigger variant. | |
| 123 int candidate_index = -1; | |
| 124 int min_area = INT_MAX; | |
| 125 for (size_t i = 0; i < candidate_sizes.size(); ++i) { | |
| 126 int area = candidate_sizes[i].GetArea(); | |
| 127 if (candidate_sizes[i].width() > desired_size && | |
| 128 candidate_sizes[i].height() > desired_size && | |
| 129 (candidate_index == -1 || area < min_area)) { | |
| 130 candidate_index = i; | |
| 131 min_area = area; | |
| 132 } | |
| 133 } | |
| 134 *score = 0.1f; | |
| 135 // c) Else, from the biggest smaller variant. | |
| 136 if (candidate_index == -1) { | |
| 137 *score = 0; | |
| 138 candidate_index = BiggestCandidate(candidate_sizes); | |
| 139 } | |
| 140 | |
| 141 const gfx::Size& candidate_size = candidate_sizes[candidate_index]; | |
| 142 bool is_integer_multiple = desired_size % candidate_size.width() == 0 && | |
| 143 desired_size % candidate_size.height() == 0; | |
| 144 *resize_method = is_integer_multiple ? SAMPLE_NEAREST_NEIGHBOUR : LANCZOS; | |
| 145 return candidate_index; | |
| 146 } | |
| 147 | |
| 148 // Represents the index of the best candidate for a |scale_factor| from the | |
| 149 // |candidate_sizes| passed into GetCandidateIndicesWithBestScores(). | |
| 150 struct SelectionResult { | |
| 151 // index in |candidate_sizes| of the best candidate. | |
| 152 size_t index; | |
| 153 | |
| 154 // The ScaleFactor for which |index| is the best candidate. | |
| 155 ui::ScaleFactor scale_factor; | |
| 156 | |
| 157 // How the bitmap data that the bitmap with |candidate_sizes[index]| should | |
| 158 // be resized for displaying in the UI. | |
| 159 ResizeMethod resize_method; | |
| 160 }; | |
| 161 | |
| 162 void GetCandidateIndicesWithBestScores( | |
| 163 const std::vector<gfx::Size>& candidate_sizes, | |
| 164 const std::vector<ui::ScaleFactor>& scale_factors, | |
| 165 int desired_size, | |
| 166 float* match_score, | |
| 167 std::vector<SelectionResult>* results) { | |
| 168 if (candidate_sizes.empty()) | |
| 169 return; | |
| 170 | |
| 171 if (desired_size == 0) { | |
| 172 // Just return the biggest image available. | |
| 173 SelectionResult result; | |
| 174 result.index = BiggestCandidate(candidate_sizes); | |
| 175 result.scale_factor = ui::SCALE_FACTOR_100P; | |
| 176 result.resize_method = NONE; | |
| 177 results->push_back(result); | |
| 178 if (match_score) | |
| 179 *match_score = 0.8f; | |
| 180 return; | |
| 181 } | |
| 182 | |
| 183 float total_score = 0; | |
| 184 for (size_t i = 0; i < scale_factors.size(); ++i) { | |
| 185 float score; | |
| 186 SelectionResult result; | |
| 187 result.scale_factor = scale_factors[i]; | |
| 188 result.index = GetCandidateIndexWithBestScore(candidate_sizes, | |
| 189 result.scale_factor, desired_size, &score, &result.resize_method); | |
| 190 results->push_back(result); | |
| 191 total_score += score; | |
| 192 } | |
| 193 | |
| 194 if (match_score) | |
| 195 *match_score = total_score / scale_factors.size(); | |
| 196 } | |
| 197 | |
| 198 // Resize |source_bitmap| using |resize_method|. | |
| 199 SkBitmap GetResizedBitmap(const SkBitmap& source_bitmap, | |
| 200 int desired_size_in_dip, | |
| 201 ui::ScaleFactor scale_factor, | |
| 202 ResizeMethod resize_method) { | |
| 203 float scale = ui::GetScaleFactorScale(scale_factor); | |
| 204 int desired_size_in_pixel = static_cast<int>( | |
| 205 desired_size_in_dip * scale + 0.5f); | |
| 206 | |
| 207 switch(resize_method) { | |
| 208 case NONE: | |
| 209 return source_bitmap; | |
| 210 case PAD_WITH_BORDER: { | |
| 211 int inner_border_in_pixel = static_cast<int>(16 * scale + 0.5f); | |
| 212 return PadWithBorder(source_bitmap, desired_size_in_pixel, | |
| 213 inner_border_in_pixel); | |
| 214 } | |
| 215 case SAMPLE_NEAREST_NEIGHBOUR: | |
| 216 return SampleNearestNeighbor(source_bitmap, desired_size_in_pixel); | |
| 217 case LANCZOS: | |
| 218 return skia::ImageOperations::Resize( | |
| 219 source_bitmap, skia::ImageOperations::RESIZE_LANCZOS3, | |
| 220 desired_size_in_pixel, desired_size_in_pixel); | |
| 221 } | |
| 222 return source_bitmap; | |
| 223 } | |
| 224 | |
| 225 } // namespace | |
| 226 | |
| 227 gfx::ImageSkia SelectFaviconFrames( | |
| 228 const std::vector<SkBitmap>& bitmaps, | |
| 229 const std::vector<ui::ScaleFactor>& scale_factors, | |
| 230 int desired_size, | |
| 231 float* match_score) { | |
| 232 std::vector<gfx::Size> candidate_sizes; | |
| 233 SizesFromBitmaps(bitmaps, &candidate_sizes); | |
| 234 | |
| 235 std::vector<SelectionResult> results; | |
| 236 GetCandidateIndicesWithBestScores(candidate_sizes, scale_factors, | |
| 237 desired_size, match_score, &results); | |
| 238 | |
| 239 gfx::ImageSkia multi_image; | |
| 240 for (size_t i = 0; i < results.size(); ++i) { | |
| 241 const SelectionResult& result = results[i]; | |
| 242 SkBitmap resized_bitmap = GetResizedBitmap(bitmaps[result.index], | |
| 243 desired_size, result.scale_factor, result.resize_method); | |
| 244 multi_image.AddRepresentation( | |
| 245 gfx::ImageSkiaRep(resized_bitmap, result.scale_factor)); | |
| 246 } | |
| 247 return multi_image; | |
| 248 } | |
| OLD | NEW |