blob: a9c19939481ee5a1e7378cea50d28b6c7c112380 [file] [log] [blame]
khmel8c1f6622017-05-11 19:14:501// Copyright 2017 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/extensions/chrome_app_icon.h"
6
7#include <algorithm>
8
9#include "chrome/browser/extensions/chrome_app_icon_delegate.h"
10#include "chrome/browser/extensions/extension_util.h"
11#include "extensions/browser/extension_registry.h"
12#include "extensions/common/manifest_handlers/icons_handler.h"
13#include "ui/gfx/canvas.h"
14#include "ui/gfx/geometry/rect.h"
15#include "ui/gfx/image/canvas_image_source.h"
16#include "ui/gfx/image/image_skia_operations.h"
17
18#if defined(OS_CHROMEOS)
19#include "chrome/browser/chromeos/extensions/gfx_utils.h"
20#endif
21
22namespace extensions {
23
24namespace {
25
26// Rounds the corners of a given image.
27// TODO(khmel): avoid sub-classing CanvasImageSource.
28class RoundedCornersImageSource : public gfx::CanvasImageSource {
29 public:
30 explicit RoundedCornersImageSource(const gfx::ImageSkia& icon)
Peter Kasting0aa18542019-06-22 05:35:0131 : gfx::CanvasImageSource(icon.size()), icon_(icon) {}
khmel8c1f6622017-05-11 19:14:5032 ~RoundedCornersImageSource() override {}
33
34 private:
35 // gfx::CanvasImageSource overrides:
36 void Draw(gfx::Canvas* canvas) override {
37 // The radius used to round the app icon, based on 2 pixel per 48 pixels
38 // icon size.
39 const int rounding_radius =
40 std::max<int>(std::round(2.0 * icon_.width() / 48.0), 1);
41
42 canvas->DrawImageInt(icon_, 0, 0);
43
44 cc::PaintFlags masking_flags;
45 masking_flags.setBlendMode(SkBlendMode::kDstIn);
46 canvas->SaveLayerWithFlags(masking_flags);
47
48 cc::PaintFlags mask_flags;
49 mask_flags.setAntiAlias(true);
50 mask_flags.setColor(SK_ColorWHITE);
51 canvas->DrawRoundRect(gfx::Rect(icon_.width(), icon_.height()),
52 rounding_radius, mask_flags);
53
54 canvas->Restore();
55 }
56
57 gfx::ImageSkia icon_;
58
59 DISALLOW_COPY_AND_ASSIGN(RoundedCornersImageSource);
60};
61
62} // namespace
63
Nigel Tao22ba05a2018-12-12 01:32:5164// static
65void ChromeAppIcon::ApplyEffects(int resource_size_in_dip,
66 const ResizeFunction& resize_function,
67 bool apply_chrome_badge,
68 bool app_launchable,
69 bool from_bookmark,
70 gfx::ImageSkia* image_skia) {
71 if (!resize_function.is_null()) {
72 resize_function.Run(gfx::Size(resource_size_in_dip, resource_size_in_dip),
73 image_skia);
74 }
75
76#if defined(OS_CHROMEOS)
77 if (apply_chrome_badge) {
78 util::ApplyChromeBadge(image_skia);
79 }
80#endif
81
82 if (!app_launchable) {
83 constexpr color_utils::HSL shift = {-1, 0, 0.6};
84 *image_skia =
85 gfx::ImageSkiaOperations::CreateHSLShiftedImage(*image_skia, shift);
86 }
87
88 if (from_bookmark) {
89 *image_skia =
90 gfx::ImageSkia(std::make_unique<RoundedCornersImageSource>(*image_skia),
91 image_skia->size());
92 }
93}
94
khmel8c1f6622017-05-11 19:14:5095ChromeAppIcon::ChromeAppIcon(ChromeAppIconDelegate* delegate,
96 content::BrowserContext* browser_context,
97 DestroyedCallback destroyed_callback,
98 const std::string& app_id,
Vladislav Kaznacheev6ae79b42018-09-13 06:11:1199 int resource_size_in_dip,
100 const ResizeFunction& resize_function)
khmel8c1f6622017-05-11 19:14:50101 : delegate_(delegate),
102 browser_context_(browser_context),
103 destroyed_callback_(std::move(destroyed_callback)),
104 app_id_(app_id),
Vladislav Kaznacheev6ae79b42018-09-13 06:11:11105 resource_size_in_dip_(resource_size_in_dip),
106 resize_function_(resize_function) {
khmel8c1f6622017-05-11 19:14:50107 DCHECK(delegate_);
108 DCHECK(browser_context_);
109 DCHECK(!destroyed_callback_.is_null());
110 DCHECK_GE(resource_size_in_dip, 0);
111 Reload();
112}
113
114ChromeAppIcon::~ChromeAppIcon() {
115 std::move(destroyed_callback_).Run(this);
116}
117
118const Extension* ChromeAppIcon::GetExtension() {
119 return ExtensionRegistry::Get(browser_context_)
120 ->GetInstalledExtension(app_id_);
121}
122
123void ChromeAppIcon::Reload() {
124 const Extension* extension = GetExtension();
khmel0df822022017-06-23 02:02:56125 const gfx::ImageSkia default_icon = extension && extension->is_app()
126 ? util::GetDefaultAppIcon()
127 : util::GetDefaultExtensionIcon();
Jinho Bangb5216cec2018-01-17 19:43:11128 icon_ = std::make_unique<IconImage>(
khmel1ddcada2017-06-02 19:47:09129 browser_context_, extension,
130 extension ? IconsInfo::GetIcons(extension) : ExtensionIconSet(),
Vladislav Kaznacheev6ae79b42018-09-13 06:11:11131 resource_size_in_dip_, !resize_function_.is_null(), default_icon, this);
khmel8c1f6622017-05-11 19:14:50132 UpdateIcon();
133}
134
135bool ChromeAppIcon::IsValid() const {
136 DCHECK(icon_);
137 return icon_->is_valid();
138}
139
140void ChromeAppIcon::UpdateIcon() {
141 DCHECK(icon_);
142
143 image_skia_ = icon_->image_skia();
Nigel Tao22ba05a2018-12-12 01:32:51144
145 bool apply_chrome_badge = false;
khmel8c1f6622017-05-11 19:14:50146#if defined(OS_CHROMEOS)
Nigel Tao22ba05a2018-12-12 01:32:51147 icon_is_badged_ = util::ShouldApplyChromeBadge(browser_context_, app_id_);
148 apply_chrome_badge = icon_is_badged_;
khmel8c1f6622017-05-11 19:14:50149#endif
150
Nigel Tao22ba05a2018-12-12 01:32:51151 bool app_launchable = util::IsAppLaunchable(app_id_, browser_context_);
khmel8c1f6622017-05-11 19:14:50152
Nigel Tao22ba05a2018-12-12 01:32:51153 const Extension* extension =
154 ExtensionRegistry::Get(browser_context_)->GetInstalledExtension(app_id_);
155 bool from_bookmark = extension && extension->from_bookmark();
156
157 ApplyEffects(resource_size_in_dip_, resize_function_, apply_chrome_badge,
158 app_launchable, from_bookmark, &image_skia_);
khmel8c1f6622017-05-11 19:14:50159
160 delegate_->OnIconUpdated(this);
161}
162
163void ChromeAppIcon::OnExtensionIconImageChanged(IconImage* icon) {
164 DCHECK_EQ(icon_.get(), icon);
165 UpdateIcon();
166}
167
168} // namespace extensions