File: icon_badging.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (320 lines) | stat: -rw-r--r-- 11,827 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/shortcuts/icon_badging.h"

#include <algorithm>
#include <cmath>
#include <vector>

#include "base/check_op.h"
#include "base/containers/contains.h"
#include "base/containers/enum_set.h"
#include "base/containers/fixed_flat_map.h"
#include "base/containers/flat_map.h"
#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/version_info/channel.h"
#include "build/branding_buildflags.h"
#include "build/buildflag.h"
#include "chrome/common/channel_info.h"
#include "chrome/grit/chrome_unscaled_resources.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkImage.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_family.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_operations.h"

namespace shortcuts {

namespace {

enum class ShortcutSize {
  k16,
  k32,
  k48,
  k128,
  k256,
  k512,
  kMaxValue = k512
};

enum class BadgeSize {
  k8,
  k16,
  k24,
  k64,
  k128,
  kMaxValue = k128
};

// Returns the icon sizes needed for shortcut creation on the desktop. k32 is
// needed for the icon in the Create Shortcut dialog.
#if BUILDFLAG(IS_MAC)
constexpr ShortcutSize kSizesNeededForShortcutCreation[] = {
    ShortcutSize::k16, ShortcutSize::k32, ShortcutSize::k128,
    ShortcutSize::k256, ShortcutSize::k512};
#elif BUILDFLAG(IS_LINUX)
constexpr ShortcutSize kSizesNeededForShortcutCreation[] = {ShortcutSize::k32,
                                                            ShortcutSize::k128};
#elif BUILDFLAG(IS_WIN)
constexpr ShortcutSize kSizesNeededForShortcutCreation[] = {
    ShortcutSize::k16, ShortcutSize::k32, ShortcutSize::k48,
    ShortcutSize::k256};
#endif

int ToInt(ShortcutSize size) {
  switch (size) {
    case ShortcutSize::k16:
      return 16;
    case ShortcutSize::k32:
      return 32;
    case ShortcutSize::k48:
      return 48;
    case ShortcutSize::k128:
      return 128;
    case ShortcutSize::k256:
      return 256;
    case ShortcutSize::k512:
      return 512;
  }
}

int ToInt(BadgeSize size) {
  switch (size) {
    case BadgeSize::k8:
      return 8;
    case BadgeSize::k16:
      return 16;
    case BadgeSize::k24:
      return 24;
    case BadgeSize::k64:
      return 64;
    case BadgeSize::k128:
      return 128;
  }
}

// For icon sizes > 128, use a badge size that is 1/4, else use a badge size
// that is 1/2.
BadgeSize GetBadgeSizeFromShortcutSize(ShortcutSize icon_size) {
  switch (icon_size) {
    case ShortcutSize::k16:
      return BadgeSize::k8;
    case ShortcutSize::k32:
      return BadgeSize::k16;
    case ShortcutSize::k48:
      return BadgeSize::k24;
    case ShortcutSize::k128:
      return BadgeSize::k64;
    case ShortcutSize::k256:
      return BadgeSize::k64;
    case ShortcutSize::k512:
      return BadgeSize::k128;
  }
}

constexpr int resource_map_size = static_cast<int>(BadgeSize::kMaxValue) + 1;

using SizeToResourceMap = base::fixed_flat_map<BadgeSize, int, 5>;

// At very low pixels < 16, there is probably no need to recreate the
// resource files necessary for these icons. Resizing them instead would
// work just as better, without the need to increase the chrome binary
// size.
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
constexpr SizeToResourceMap kStableResourceMap =
    base::MakeFixedFlatMap<BadgeSize, int>(
        {{BadgeSize::k8, IDR_PRODUCT_LOGO_16_STABLE_SHORTCUTS},
         {BadgeSize::k16, IDR_PRODUCT_LOGO_16_STABLE_SHORTCUTS},
         {BadgeSize::k24, IDR_PRODUCT_LOGO_24_STABLE_SHORTCUTS},
         {BadgeSize::k64, IDR_PRODUCT_LOGO_64_STABLE_SHORTCUTS},
         {BadgeSize::k128, IDR_PRODUCT_LOGO_128_STABLE_SHORTCUTS}});
constexpr SizeToResourceMap kCanaryResourceMap =
    base::MakeFixedFlatMap<BadgeSize, int>(
        {{BadgeSize::k8, IDR_PRODUCT_LOGO_CANARY_16_SHORTCUTS},
         {BadgeSize::k16, IDR_PRODUCT_LOGO_CANARY_16_SHORTCUTS},
         {BadgeSize::k24, IDR_PRODUCT_LOGO_CANARY_24_SHORTCUTS},
         {BadgeSize::k64, IDR_PRODUCT_LOGO_CANARY_64_SHORTCUTS},
         {BadgeSize::k128, IDR_PRODUCT_LOGO_CANARY_128_SHORTCUTS}});
constexpr SizeToResourceMap kBetaResourceMap =
    base::MakeFixedFlatMap<BadgeSize, int>(
        {{BadgeSize::k8, IDR_PRODUCT_LOGO_BETA_16_SHORTCUTS},
         {BadgeSize::k16, IDR_PRODUCT_LOGO_BETA_16_SHORTCUTS},
         {BadgeSize::k24, IDR_PRODUCT_LOGO_BETA_24_SHORTCUTS},
         {BadgeSize::k64, IDR_PRODUCT_LOGO_BETA_64_SHORTCUTS},
         {BadgeSize::k128, IDR_PRODUCT_LOGO_BETA_128_SHORTCUTS}});
constexpr SizeToResourceMap kDevResourceMap =
    base::MakeFixedFlatMap<BadgeSize, int>(
        {{BadgeSize::k8, IDR_PRODUCT_LOGO_DEV_16_SHORTCUTS},
         {BadgeSize::k16, IDR_PRODUCT_LOGO_DEV_16_SHORTCUTS},
         {BadgeSize::k24, IDR_PRODUCT_LOGO_DEV_24_SHORTCUTS},
         {BadgeSize::k64, IDR_PRODUCT_LOGO_DEV_64_SHORTCUTS},
         {BadgeSize::k128, IDR_PRODUCT_LOGO_DEV_128_SHORTCUTS}});

static_assert(static_cast<int>(kStableResourceMap.size()) == resource_map_size,
              "Ensure icon resources are filled for all entries in BadgeSize "
              "for stable resource map");
static_assert(static_cast<int>(kCanaryResourceMap.size()) == resource_map_size,
              "Ensure icon resources are filled for all entries in BadgeSize "
              "for canary resource map");
static_assert(static_cast<int>(kBetaResourceMap.size()) == resource_map_size,
              "Ensure icon resources are filled for all entries in BadgeSize "
              "for beta resource map");
static_assert(static_cast<int>(kDevResourceMap.size()) == resource_map_size,
              "Ensure icon resources are filled for all entries in BadgeSize "
              "for dev resource map");

#elif BUILDFLAG(GOOGLE_CHROME_FOR_TESTING_BRANDING)
constexpr SizeToResourceMap kChromeForTestingBrandedResourceMap =
    base::MakeFixedFlatMap<BadgeSize, int>(
        {{BadgeSize::k8, IDR_PRODUCT_LOGO_16_CFT_SHORTCUTS},
         {BadgeSize::k16, IDR_PRODUCT_LOGO_16_CFT_SHORTCUTS},
         {BadgeSize::k24, IDR_PRODUCT_LOGO_24_CFT_SHORTCUTS},
         {BadgeSize::k64, IDR_PRODUCT_LOGO_64_CFT_SHORTCUTS},
         {BadgeSize::k128, IDR_PRODUCT_LOGO_128_CFT_SHORTCUTS}});

static_assert(static_cast<int>(kChromeForTestingBrandedResourceMap.size()) ==
                  resource_map_size,
              "Ensure icon resources are filled for all entries in BadgeSize "
              "for Chrome for testing resource map");
#else
constexpr SizeToResourceMap kChromiumResourceMap =
    base::MakeFixedFlatMap<BadgeSize, int>(
        {{BadgeSize::k8, IDR_PRODUCT_LOGO_16_SHORTCUTS},
         {BadgeSize::k16, IDR_PRODUCT_LOGO_16_SHORTCUTS},
         {BadgeSize::k24, IDR_PRODUCT_LOGO_24_SHORTCUTS},
         {BadgeSize::k64, IDR_PRODUCT_LOGO_64_SHORTCUTS},
         {BadgeSize::k128, IDR_PRODUCT_LOGO_128_SHORTCUTS}});

static_assert(static_cast<int>(kChromiumResourceMap.size()) ==
                  resource_map_size,
              "Ensure icon resources are filled for all entries in BadgeSize "
              "for chromium resource map");
#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)

SizeToResourceMap GetResourceMapForCurrentChannel() {
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
  switch (chrome::GetChannel()) {
    // |version_info::Channel::DEFAULT| is seen on local builds with
    // is_chrome_branded = true.
    case version_info::Channel::DEFAULT:
    case version_info::Channel::STABLE:
      return kStableResourceMap;
    case version_info::Channel::CANARY:
      return kCanaryResourceMap;
    case version_info::Channel::DEV:
      return kDevResourceMap;
    case version_info::Channel::BETA:
      return kBetaResourceMap;
  }
#elif BUILDFLAG(GOOGLE_CHROME_FOR_TESTING_BRANDING)
  return kChromeForTestingBrandedResourceMap;
#else
  return kChromiumResourceMap;
#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
}

gfx::ImageSkia GetMaskForBadging(ShortcutSize icon_size) {
  BadgeSize badge_size = GetBadgeSizeFromShortcutSize(icon_size);

  gfx::ImageSkia* image =
      ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
          GetResourceMapForCurrentChannel().at(badge_size));
  CHECK(image);

  // TODO(crbug.com/337998776): Update if assets are sent by UX.
  const float kWhiteMaskFactor = 1.75f;
  // Set white mask for the loaded icon.
  gfx::ImageSkia masked_badge =
      gfx::ImageSkiaOperations::CreateImageWithCircleBackground(
          image->width() / kWhiteMaskFactor, SK_ColorWHITE, *image);

  int badge_size_int = ToInt(badge_size);
  // Maintain the badging ratio of 1/4th for sizes < 128, and 1/8th
  // for sizes greater. Adding the white background after loading the resource
  // will likely throw that ratio off.
  gfx::ImageSkia resized_post_masking =
      gfx::ImageSkiaOperations::CreateResizedImage(
          masked_badge, skia::ImageOperations::ResizeMethod::RESIZE_LANCZOS3,
          gfx::Size(badge_size_int, badge_size_int));

  return resized_post_masking;
}

int FindClosestIconSizeToUse(const std::vector<int>& sorted_icons,
                             int size_to_use) {
  auto closest_size_iter =
      std::lower_bound(sorted_icons.begin(), sorted_icons.end(), size_to_use);

  // Iterator out of bounds, can happen if the input icon size is too large,
  // like say 1000.
  if (closest_size_iter == sorted_icons.end()) {
    closest_size_iter = sorted_icons.end() - 1;
  } else if (closest_size_iter != sorted_icons.begin()) {
    // Compare with the iterator found and the one just before, and choose the
    // size that is closer. This helps reduce the pixelation when the icon gets
    // resized ultimately.
    int size_before = *(closest_size_iter - 1);
    int size_after = *(closest_size_iter);
    if (std::fabs(size_to_use - size_before) <
        std::fabs(size_to_use - size_after)) {
      --closest_size_iter;
    }
  }

  return *closest_size_iter;
}

using ShortcutSizes =
    base::EnumSet<ShortcutSize, ShortcutSize::k16, ShortcutSize::k512>;
}  // namespace

gfx::ImageFamily ApplyProductLogoBadgeToIcons(std::vector<SkBitmap> icons) {
  gfx::ImageFamily badged_icons;
  CHECK(!icons.empty());

  base::flat_map<int, SkBitmap> sorted_icons;
  std::vector<int> icon_sizes;
  for (const auto& icon : icons) {
    sorted_icons.insert_or_assign(icon.width(), icon);
    icon_sizes.push_back(icon.width());
  }

  std::sort(icon_sizes.begin(), icon_sizes.end());

  for (const ShortcutSize needed_size : kSizesNeededForShortcutCreation) {
    int icon_size = ToInt(needed_size);
    int size_to_use = FindClosestIconSizeToUse(icon_sizes, icon_size);
    SkBitmap bitmap_to_use = sorted_icons.at(size_to_use);

    // Resize the current bitmap to the needed_size, so that the properly
    // selected product logo does not get pixellated.
    gfx::ImageSkia resized_to_fit =
        gfx::ImageSkiaOperations::CreateResizedImage(
            gfx::ImageSkia::CreateFrom1xBitmap(bitmap_to_use),
            skia::ImageOperations::ResizeMethod::RESIZE_BEST,
            gfx::Size(icon_size, icon_size));

    gfx::ImageSkia masked_badge = GetMaskForBadging(needed_size);

    // Apply the masked product logo to the bitmaps.
    gfx::ImageSkia badged_icon = gfx::ImageSkiaOperations::CreateIconWithBadge(
        resized_to_fit, masked_badge);

    // Doing this allows the returned gfx::ImageFamily to be passed across
    // multiple sequences, like when this is passed to the ThreadPool during
    // shortcut creation at the OS level.
    badged_icon.MakeThreadSafe();

    badged_icons.Add(badged_icon);
  }
  return badged_icons;
}

}  // namespace shortcuts