1 // Copyright 2018 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 "ash/public/cpp/network_icon_image_source.h"
6
7 #include "ash/public/cpp/ash_constants.h"
8 #include "third_party/skia/include/core/SkPath.h"
9 #include "ui/gfx/canvas.h"
10 #include "ui/gfx/geometry/insets.h"
11 #include "ui/gfx/geometry/rect.h"
12 #include "ui/gfx/geometry/rect_f.h"
13 #include "ui/gfx/paint_vector_icon.h"
14 #include "ui/gfx/scoped_canvas.h"
15 #include "ui/gfx/skia_util.h"
16 #include "ui/gfx/vector_icon_types.h"
17
18 namespace ash {
19 namespace network_icon {
20
21 namespace {
22
23 constexpr int kIconStrokeWidth = 2;
24 constexpr int kCellularIconOffset = 1;
25
CreateArcPath(gfx::RectF oval,float start_angle,float sweep_angle)26 SkPath CreateArcPath(gfx::RectF oval, float start_angle, float sweep_angle) {
27 SkPath path;
28 path.setIsVolatile(true);
29 path.setFillType(SkPathFillType::kWinding);
30 path.moveTo(oval.CenterPoint().x(), oval.CenterPoint().y());
31 path.arcTo(gfx::RectFToSkRect(oval), start_angle, sweep_angle, false);
32 path.close();
33 return path;
34 }
35
36 } // namespace
37
38 //------------------------------------------------------------------------------
39 // NetworkIconImageSource
40
NetworkIconImageSource(const gfx::Size & size,const gfx::ImageSkia & icon,const Badges & badges)41 NetworkIconImageSource::NetworkIconImageSource(const gfx::Size& size,
42 const gfx::ImageSkia& icon,
43 const Badges& badges)
44 : CanvasImageSource(size), icon_(icon), badges_(badges) {}
45
46 NetworkIconImageSource::~NetworkIconImageSource() = default;
47
Draw(gfx::Canvas * canvas)48 void NetworkIconImageSource::Draw(gfx::Canvas* canvas) {
49 const int width = size().width();
50 const int height = size().height();
51
52 // The base icon is centered in both dimensions.
53 const int icon_x = (width - icon_.width()) / 2;
54 const int icon_y = (height - icon_.height()) / 2;
55 canvas->DrawImageInt(icon_, icon_x, icon_y);
56
57 auto paint_badge = [&canvas](const Badge& badge, int x, int y,
58 int badge_size = 0) {
59 gfx::ScopedCanvas scoped(canvas);
60 canvas->Translate(gfx::Vector2d(x, y));
61 if (badge_size)
62 gfx::PaintVectorIcon(canvas, *badge.icon, badge_size, badge.color);
63 else
64 gfx::PaintVectorIcon(canvas, *badge.icon, badge.color);
65 };
66
67 // The center badge is scaled and centered over the icon.
68 if (badges_.center.icon)
69 paint_badge(badges_.center, icon_x, icon_y, icon_.width());
70
71 if (badges_.top_left.icon)
72 paint_badge(badges_.top_left, 0, icon_y);
73 if (badges_.bottom_left.icon) {
74 paint_badge(
75 badges_.bottom_left, 0,
76 height - gfx::GetDefaultSizeOfVectorIcon(*badges_.bottom_left.icon));
77 }
78 if (badges_.bottom_right.icon) {
79 const int badge_offset =
80 gfx::GetDefaultSizeOfVectorIcon(*badges_.bottom_right.icon) - 1;
81 paint_badge(badges_.bottom_right, width - badge_offset,
82 height - badge_offset);
83 }
84 }
85
HasRepresentationAtAllScales() const86 bool NetworkIconImageSource::HasRepresentationAtAllScales() const {
87 return true;
88 }
89
90 //------------------------------------------------------------------------------
91 // SignalStrengthImageSource
92
SignalStrengthImageSource(ImageType image_type,SkColor color,const gfx::Size & size,int signal_strength,int padding)93 SignalStrengthImageSource::SignalStrengthImageSource(ImageType image_type,
94 SkColor color,
95 const gfx::Size& size,
96 int signal_strength,
97 int padding)
98 : CanvasImageSource(size),
99 image_type_(image_type),
100 color_(color),
101 signal_strength_(signal_strength),
102 padding_(padding) {
103 if (image_type_ == NONE)
104 image_type_ = ARCS;
105
106 DCHECK_GE(signal_strength, 0);
107 DCHECK_LT(signal_strength, kNumNetworkImages);
108 }
109
110 SignalStrengthImageSource::~SignalStrengthImageSource() = default;
111
112 // gfx::CanvasImageSource:
Draw(gfx::Canvas * canvas)113 void SignalStrengthImageSource::Draw(gfx::Canvas* canvas) {
114 if (image_type_ == ARCS)
115 DrawArcs(canvas);
116 else
117 DrawBars(canvas);
118 }
119
HasRepresentationAtAllScales() const120 bool SignalStrengthImageSource::HasRepresentationAtAllScales() const {
121 return true;
122 }
123
DrawArcs(gfx::Canvas * canvas)124 void SignalStrengthImageSource::DrawArcs(gfx::Canvas* canvas) {
125 gfx::RectF oval_bounds((gfx::Rect(size())));
126 oval_bounds.Inset(gfx::Insets(padding_));
127 // Double the width and height. The new midpoint should be the former
128 // bottom center.
129 oval_bounds.Inset(-oval_bounds.width() / 2, 0, -oval_bounds.width() / 2,
130 -oval_bounds.height());
131
132 constexpr SkScalar kAngleAboveHorizontal = 51.f;
133 constexpr SkScalar kStartAngle = 180.f + kAngleAboveHorizontal;
134 constexpr SkScalar kSweepAngle = 180.f - 2 * kAngleAboveHorizontal;
135
136 cc::PaintFlags flags;
137 flags.setAntiAlias(true);
138 flags.setStyle(cc::PaintFlags::kStroke_Style);
139 flags.setStrokeWidth(kIconStrokeWidth);
140 flags.setColor(color_);
141
142 // Background (outline)
143 canvas->sk_canvas()->drawPath(
144 CreateArcPath(oval_bounds, kStartAngle, kSweepAngle), flags);
145
146 // Foreground (signal strength).
147 if (signal_strength_ != 0) {
148 flags.setStyle(cc::PaintFlags::kFill_Style);
149 // Percent of the height of the background wedge that we draw the
150 // foreground wedge, indexed by signal strength.
151 static constexpr float kWedgeHeightPercentages[] = {0.f, 0.375f, 0.5833f,
152 0.75f, 1.f};
153 const float wedge_percent = kWedgeHeightPercentages[signal_strength_];
154 oval_bounds.Inset(
155 gfx::InsetsF((oval_bounds.height() / 2) * (1.f - wedge_percent)));
156 canvas->sk_canvas()->drawPath(
157 CreateArcPath(oval_bounds, kStartAngle, kSweepAngle), flags);
158 }
159 }
160
DrawBars(gfx::Canvas * canvas)161 void SignalStrengthImageSource::DrawBars(gfx::Canvas* canvas) {
162 // Undo the canvas's device scaling and round values to the nearest whole
163 // number so we can draw on exact pixel boundaries.
164 const float dsf = canvas->UndoDeviceScaleFactor();
165 auto scale = [dsf](SkScalar dimension) {
166 return std::round(dimension * dsf);
167 };
168
169 // Length of short side of an isosceles right triangle, in dip.
170 const SkScalar kFullTriangleSide =
171 SkIntToScalar(size().width()) - padding_ * 2;
172
173 auto make_triangle = [scale, kFullTriangleSide, this](SkScalar side) {
174 SkPath triangle;
175 triangle.moveTo(scale(padding_ + kCellularIconOffset),
176 scale(padding_ + kFullTriangleSide + kCellularIconOffset));
177 triangle.rLineTo(scale(side), 0);
178 triangle.rLineTo(0, -scale(side));
179 triangle.close();
180 return triangle;
181 };
182
183 cc::PaintFlags flags;
184 flags.setAntiAlias(true);
185 flags.setColor(color_);
186
187 // Background.
188 flags.setStyle(cc::PaintFlags::kStroke_Style);
189 flags.setStrokeWidth(kIconStrokeWidth);
190
191 canvas->DrawPath(make_triangle(kFullTriangleSide), flags);
192
193 // Foreground (signal strength).
194 if (signal_strength_ != 0) {
195 flags.setStyle(cc::PaintFlags::kFill_Style);
196 // As a percentage of the bg triangle, the length of one of the short
197 // sides of the fg triangle, indexed by signal strength.
198 static constexpr float kTriangleSidePercents[] = {0.f, 0.375f, 0.5833f,
199 0.75f, 1.f};
200 canvas->DrawPath(make_triangle(kTriangleSidePercents[signal_strength_] *
201 kFullTriangleSide),
202 flags);
203 }
204 }
205
206 //------------------------------------------------------------------------------
207
GetImageForWifiNetwork(SkColor color,gfx::Size size)208 gfx::ImageSkia GetImageForWifiNetwork(SkColor color, gfx::Size size) {
209 return gfx::CanvasImageSource::MakeImageSkia<SignalStrengthImageSource>(
210 ARCS, color, size, kNumNetworkImages - 1);
211 }
212
213 } // namespace network_icon
214 } // namespace ash
215