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