[Win] Add a fast profile switcher to the Windows taskbar item.
[chromium-blink-merge.git] / chrome / browser / profiles / profile_avatar_icon_util.cc
blob6c9ea0b39ad187707b17fcabd8c02d6dd26baeb6
1 // Copyright 2014 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.
5 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
7 #include "base/files/file_util.h"
8 #include "base/format_macros.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/path_service.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/stringprintf.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/profiles/profile_info_cache.h"
15 #include "chrome/browser/profiles/profile_manager.h"
16 #include "chrome/common/chrome_paths.h"
17 #include "grit/theme_resources.h"
18 #include "skia/ext/image_operations.h"
19 #include "third_party/skia/include/core/SkPaint.h"
20 #include "third_party/skia/include/core/SkPath.h"
21 #include "third_party/skia/include/core/SkScalar.h"
22 #include "third_party/skia/include/core/SkXfermode.h"
23 #include "ui/base/resource/resource_bundle.h"
24 #include "ui/gfx/canvas.h"
25 #include "ui/gfx/image/canvas_image_source.h"
26 #include "ui/gfx/image/image.h"
27 #include "ui/gfx/image/image_skia_operations.h"
28 #include "ui/gfx/rect.h"
29 #include "ui/gfx/skia_util.h"
31 // Helper methods for transforming and drawing avatar icons.
32 namespace {
34 // Determine what the scaled height of the avatar icon should be for a
35 // specified width, to preserve the aspect ratio.
36 int GetScaledAvatarHeightForWidth(int width, const gfx::ImageSkia& avatar) {
37 // Multiply the width by the inverted aspect ratio (height over
38 // width), and then add 0.5 to ensure the int truncation rounds nicely.
39 int scaled_height = width *
40 ((float) avatar.height() / (float) avatar.width()) + 0.5f;
41 return scaled_height;
44 // A CanvasImageSource that draws a sized and positioned avatar with an
45 // optional border independently of the scale factor.
46 class AvatarImageSource : public gfx::CanvasImageSource {
47 public:
48 enum AvatarPosition {
49 POSITION_CENTER,
50 POSITION_BOTTOM_CENTER,
53 enum AvatarBorder {
54 BORDER_NONE,
55 BORDER_NORMAL,
56 BORDER_ETCHED,
59 AvatarImageSource(gfx::ImageSkia avatar,
60 const gfx::Size& canvas_size,
61 int width,
62 AvatarPosition position,
63 AvatarBorder border);
64 ~AvatarImageSource() override;
66 // CanvasImageSource override:
67 void Draw(gfx::Canvas* canvas) override;
69 private:
70 gfx::ImageSkia avatar_;
71 const gfx::Size canvas_size_;
72 const int width_;
73 const int height_;
74 const AvatarPosition position_;
75 const AvatarBorder border_;
77 DISALLOW_COPY_AND_ASSIGN(AvatarImageSource);
80 AvatarImageSource::AvatarImageSource(gfx::ImageSkia avatar,
81 const gfx::Size& canvas_size,
82 int width,
83 AvatarPosition position,
84 AvatarBorder border)
85 : gfx::CanvasImageSource(canvas_size, false),
86 canvas_size_(canvas_size),
87 width_(width),
88 height_(GetScaledAvatarHeightForWidth(width, avatar)),
89 position_(position),
90 border_(border) {
91 avatar_ = gfx::ImageSkiaOperations::CreateResizedImage(
92 avatar, skia::ImageOperations::RESIZE_BEST,
93 gfx::Size(width_, height_));
96 AvatarImageSource::~AvatarImageSource() {
99 void AvatarImageSource::Draw(gfx::Canvas* canvas) {
100 // Center the avatar horizontally.
101 int x = (canvas_size_.width() - width_) / 2;
102 int y;
104 if (position_ == POSITION_CENTER) {
105 // Draw the avatar centered on the canvas.
106 y = (canvas_size_.height() - height_) / 2;
107 } else {
108 // Draw the avatar on the bottom center of the canvas, leaving 1px below.
109 y = canvas_size_.height() - height_ - 1;
112 canvas->DrawImageInt(avatar_, x, y);
114 // The border should be square.
115 int border_size = std::max(width_, height_);
116 // Reset the x and y for the square border.
117 x = (canvas_size_.width() - border_size) / 2;
118 y = (canvas_size_.height() - border_size) / 2;
120 if (border_ == BORDER_NORMAL) {
121 // Draw a gray border on the inside of the avatar.
122 SkColor border_color = SkColorSetARGB(83, 0, 0, 0);
124 // Offset the rectangle by a half pixel so the border is drawn within the
125 // appropriate pixels no matter the scale factor. Subtract 1 from the right
126 // and bottom sizes to specify the endpoints, yielding -0.5.
127 SkPath path;
128 path.addRect(SkFloatToScalar(x + 0.5f), // left
129 SkFloatToScalar(y + 0.5f), // top
130 SkFloatToScalar(x + border_size - 0.5f), // right
131 SkFloatToScalar(y + border_size - 0.5f)); // bottom
133 SkPaint paint;
134 paint.setColor(border_color);
135 paint.setStyle(SkPaint::kStroke_Style);
136 paint.setStrokeWidth(SkIntToScalar(1));
138 canvas->DrawPath(path, paint);
139 } else if (border_ == BORDER_ETCHED) {
140 // Give the avatar an etched look by drawing a highlight on the bottom and
141 // right edges.
142 SkColor shadow_color = SkColorSetARGB(83, 0, 0, 0);
143 SkColor highlight_color = SkColorSetARGB(96, 255, 255, 255);
145 SkPaint paint;
146 paint.setStyle(SkPaint::kStroke_Style);
147 paint.setStrokeWidth(SkIntToScalar(1));
149 SkPath path;
151 // Left and top shadows. To support higher scale factors than 1, position
152 // the orthogonal dimension of each line on the half-pixel to separate the
153 // pixel. For a vertical line, this means adding 0.5 to the x-value.
154 path.moveTo(SkFloatToScalar(x + 0.5f), SkIntToScalar(y + height_));
156 // Draw up to the top-left. Stop with the y-value at a half-pixel.
157 path.rLineTo(SkIntToScalar(0), SkFloatToScalar(-height_ + 0.5f));
159 // Draw right to the top-right, stopping within the last pixel.
160 path.rLineTo(SkFloatToScalar(width_ - 0.5f), SkIntToScalar(0));
162 paint.setColor(shadow_color);
163 canvas->DrawPath(path, paint);
165 path.reset();
167 // Bottom and right highlights. Note that the shadows own the shared corner
168 // pixels, so reduce the sizes accordingly.
169 path.moveTo(SkIntToScalar(x + 1), SkFloatToScalar(y + height_ - 0.5f));
171 // Draw right to the bottom-right.
172 path.rLineTo(SkFloatToScalar(width_ - 1.5f), SkIntToScalar(0));
174 // Draw up to the top-right.
175 path.rLineTo(SkIntToScalar(0), SkFloatToScalar(-height_ + 1.5f));
177 paint.setColor(highlight_color);
178 canvas->DrawPath(path, paint);
182 } // namespace
184 namespace profiles {
186 struct IconResourceInfo {
187 int resource_id;
188 const char* filename;
191 const int kAvatarIconWidth = 38;
192 const int kAvatarIconHeight = 31;
193 const SkColor kAvatarTutorialBackgroundColor = SkColorSetRGB(0x42, 0x85, 0xf4);
194 const SkColor kAvatarTutorialContentTextColor = SkColorSetRGB(0xc6, 0xda, 0xfc);
195 const SkColor kAvatarBubbleAccountsBackgroundColor =
196 SkColorSetRGB(0xf3, 0xf3, 0xf3);
197 const SkColor kAvatarBubbleGaiaBackgroundColor =
198 SkColorSetRGB(0xf5, 0xf5, 0xf5);
199 const SkColor kUserManagerBackgroundColor = SkColorSetRGB(0xee, 0xee, 0xee);
201 const char kDefaultUrlPrefix[] = "chrome://theme/IDR_PROFILE_AVATAR_";
202 const char kGAIAPictureFileName[] = "Google Profile Picture.png";
203 const char kHighResAvatarFolderName[] = "Avatars";
205 // This avatar does not exist on the server, the high res copy is in the build.
206 const char kNoHighResAvatar[] = "NothingToDownload";
208 // The size of the function-static kDefaultAvatarIconResources array below.
209 const size_t kDefaultAvatarIconsCount = 27;
211 // The first 8 icons are generic.
212 const size_t kGenericAvatarIconsCount = 8;
214 // The avatar used as a placeholder (grey silhouette).
215 const int kPlaceholderAvatarIcon = 26;
217 gfx::Image GetSizedAvatarIcon(const gfx::Image& image,
218 bool is_rectangle,
219 int width, int height) {
220 if (!is_rectangle && image.Height() <= height)
221 return image;
223 gfx::Size size(width, height);
225 // Source for a centered, sized icon. GAIA images get a border.
226 scoped_ptr<gfx::ImageSkiaSource> source(
227 new AvatarImageSource(
228 *image.ToImageSkia(),
229 size,
230 std::min(width, height),
231 AvatarImageSource::POSITION_CENTER,
232 AvatarImageSource::BORDER_NONE));
234 return gfx::Image(gfx::ImageSkia(source.release(), size));
237 gfx::Image GetAvatarIconForMenu(const gfx::Image& image,
238 bool is_rectangle) {
239 return GetSizedAvatarIcon(
240 image, is_rectangle, kAvatarIconWidth, kAvatarIconHeight);
243 gfx::Image GetAvatarIconForWebUI(const gfx::Image& image,
244 bool is_rectangle) {
245 return GetSizedAvatarIcon(image, is_rectangle,
246 kAvatarIconWidth, kAvatarIconHeight);
249 gfx::Image GetAvatarIconForTitleBar(const gfx::Image& image,
250 bool is_gaia_image,
251 int dst_width,
252 int dst_height) {
253 // The image requires no border or resizing.
254 if (!is_gaia_image && image.Height() <= kAvatarIconHeight)
255 return image;
257 int size = std::min(std::min(kAvatarIconWidth, kAvatarIconHeight),
258 std::min(dst_width, dst_height));
259 gfx::Size dst_size(dst_width, dst_height);
261 // Source for a sized icon drawn at the bottom center of the canvas,
262 // with an etched border (for GAIA images).
263 scoped_ptr<gfx::ImageSkiaSource> source(
264 new AvatarImageSource(
265 *image.ToImageSkia(),
266 dst_size,
267 size,
268 AvatarImageSource::POSITION_BOTTOM_CENTER,
269 is_gaia_image ? AvatarImageSource::BORDER_ETCHED :
270 AvatarImageSource::BORDER_NONE));
272 return gfx::Image(gfx::ImageSkia(source.release(), dst_size));
275 SkBitmap GetAvatarIconAsSquare(const SkBitmap& source_bitmap,
276 int scale_factor) {
277 SkBitmap square_bitmap;
278 if ((source_bitmap.width() == scale_factor * profiles::kAvatarIconWidth) &&
279 (source_bitmap.height() == scale_factor * profiles::kAvatarIconHeight)) {
280 // Shave a couple of columns so the |source_bitmap| is more square. So when
281 // resized to a square aspect ratio it looks pretty.
282 gfx::Rect frame(scale_factor * profiles::kAvatarIconWidth,
283 scale_factor * profiles::kAvatarIconHeight);
284 frame.Inset(scale_factor * 2, 0, scale_factor * 2, 0);
285 source_bitmap.extractSubset(&square_bitmap, gfx::RectToSkIRect(frame));
286 } else {
287 // If not the avatar icon's aspect ratio, the image should be square.
288 DCHECK(source_bitmap.width() == source_bitmap.height());
289 square_bitmap = source_bitmap;
291 return square_bitmap;
294 void GetTransparentBackgroundProfileAvatar(const base::FilePath& profile_path,
295 gfx::Image* image,
296 bool* is_rectangle) {
297 const ProfileInfoCache& cache =
298 g_browser_process->profile_manager()->GetProfileInfoCache();
299 size_t index = cache.GetIndexOfProfileWithPath(profile_path);
300 if (index == std::string::npos) {
301 NOTREACHED();
302 return;
305 // If there is a Gaia image available, try to use that.
306 if (cache.IsUsingGAIAPictureOfProfileAtIndex(index)) {
307 const gfx::Image* gaia_image = cache.GetGAIAPictureOfProfileAtIndex(index);
308 if (gaia_image) {
309 *image = *gaia_image;
310 *is_rectangle = true;
311 return;
315 // Otherwise, use the default resource, not the downloaded high-res one.
316 const size_t icon_index = cache.GetAvatarIconIndexOfProfileAtIndex(index);
317 const int resource_id =
318 profiles::GetDefaultAvatarIconResourceIDAtIndex(icon_index);
319 *image = ResourceBundle::GetSharedInstance().GetNativeImageNamed(resource_id);
320 *is_rectangle = false;
323 // Helper methods for accessing, transforming and drawing avatar icons.
324 size_t GetDefaultAvatarIconCount() {
325 return kDefaultAvatarIconsCount;
328 size_t GetGenericAvatarIconCount() {
329 return kGenericAvatarIconsCount;
332 int GetPlaceholderAvatarIndex() {
333 return kPlaceholderAvatarIcon;
336 int GetPlaceholderAvatarIconResourceID() {
337 return IDR_PROFILE_AVATAR_26;
340 const IconResourceInfo* GetDefaultAvatarIconResourceInfo(size_t index) {
341 static const IconResourceInfo resource_info[kDefaultAvatarIconsCount] = {
342 { IDR_PROFILE_AVATAR_0, "avatar_generic.png"},
343 { IDR_PROFILE_AVATAR_1, "avatar_generic_aqua.png"},
344 { IDR_PROFILE_AVATAR_2, "avatar_generic_blue.png"},
345 { IDR_PROFILE_AVATAR_3, "avatar_generic_green.png"},
346 { IDR_PROFILE_AVATAR_4, "avatar_generic_orange.png"},
347 { IDR_PROFILE_AVATAR_5, "avatar_generic_purple.png"},
348 { IDR_PROFILE_AVATAR_6, "avatar_generic_red.png"},
349 { IDR_PROFILE_AVATAR_7, "avatar_generic_yellow.png"},
350 { IDR_PROFILE_AVATAR_8, "avatar_secret_agent.png"},
351 { IDR_PROFILE_AVATAR_9, "avatar_superhero.png"},
352 { IDR_PROFILE_AVATAR_10, "avatar_volley_ball.png"},
353 { IDR_PROFILE_AVATAR_11, "avatar_businessman.png"},
354 { IDR_PROFILE_AVATAR_12, "avatar_ninja.png"},
355 { IDR_PROFILE_AVATAR_13, "avatar_alien.png"},
356 { IDR_PROFILE_AVATAR_14, "avatar_smiley.png"},
357 { IDR_PROFILE_AVATAR_15, "avatar_flower.png"},
358 { IDR_PROFILE_AVATAR_16, "avatar_pizza.png"},
359 { IDR_PROFILE_AVATAR_17, "avatar_soccer.png"},
360 { IDR_PROFILE_AVATAR_18, "avatar_burger.png"},
361 { IDR_PROFILE_AVATAR_19, "avatar_cat.png"},
362 { IDR_PROFILE_AVATAR_20, "avatar_cupcake.png"},
363 { IDR_PROFILE_AVATAR_21, "avatar_dog.png"},
364 { IDR_PROFILE_AVATAR_22, "avatar_horse.png"},
365 { IDR_PROFILE_AVATAR_23, "avatar_margarita.png"},
366 { IDR_PROFILE_AVATAR_24, "avatar_note.png"},
367 { IDR_PROFILE_AVATAR_25, "avatar_sun_cloud.png"},
368 { IDR_PROFILE_AVATAR_26, kNoHighResAvatar},
370 return &resource_info[index];
373 int GetDefaultAvatarIconResourceIDAtIndex(size_t index) {
374 DCHECK(IsDefaultAvatarIconIndex(index));
375 return GetDefaultAvatarIconResourceInfo(index)->resource_id;
378 const char* GetDefaultAvatarIconFileNameAtIndex(size_t index) {
379 DCHECK(index < kDefaultAvatarIconsCount);
380 return GetDefaultAvatarIconResourceInfo(index)->filename;
383 const char* GetNoHighResAvatarFileName() {
384 return kNoHighResAvatar;
387 base::FilePath GetPathOfHighResAvatarAtIndex(size_t index) {
388 std::string file_name = GetDefaultAvatarIconResourceInfo(index)->filename;
389 base::FilePath user_data_dir;
390 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
391 return user_data_dir.AppendASCII(
392 kHighResAvatarFolderName).AppendASCII(file_name);
395 std::string GetDefaultAvatarIconUrl(size_t index) {
396 DCHECK(IsDefaultAvatarIconIndex(index));
397 return base::StringPrintf("%s%" PRIuS, kDefaultUrlPrefix, index);
400 bool IsDefaultAvatarIconIndex(size_t index) {
401 return index < kDefaultAvatarIconsCount;
404 bool IsDefaultAvatarIconUrl(const std::string& url, size_t* icon_index) {
405 DCHECK(icon_index);
406 if (url.find(kDefaultUrlPrefix) != 0)
407 return false;
409 int int_value = -1;
410 if (base::StringToInt(base::StringPiece(url.begin() +
411 strlen(kDefaultUrlPrefix),
412 url.end()),
413 &int_value)) {
414 if (int_value < 0 ||
415 int_value >= static_cast<int>(kDefaultAvatarIconsCount))
416 return false;
417 *icon_index = int_value;
418 return true;
421 return false;
424 } // namespace profiles