1// Copyright (c) 2012 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/extension_icon_manager.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "base/stl_util.h"
10#include "extensions/browser/image_loader.h"
11#include "extensions/common/constants.h"
12#include "extensions/common/extension.h"
13#include "extensions/common/extension_icon_set.h"
14#include "extensions/common/extension_resource.h"
15#include "extensions/common/manifest_handlers/icons_handler.h"
16#include "grit/theme_resources.h"
17#include "skia/ext/image_operations.h"
18#include "ui/base/resource/resource_bundle.h"
19#include "ui/gfx/canvas.h"
20#include "ui/gfx/color_utils.h"
21#include "ui/gfx/favicon_size.h"
22#include "ui/gfx/image/image.h"
23#include "ui/gfx/size.h"
24#include "ui/gfx/skbitmap_operations.h"
25
26namespace {
27
28// Helper function to create a new bitmap with |padding| amount of empty space
29// around the original bitmap.
30static SkBitmap ApplyPadding(const SkBitmap& source,
31                             const gfx::Insets& padding) {
32  scoped_ptr<gfx::Canvas> result(
33      new gfx::Canvas(gfx::Size(source.width() + padding.width(),
34                                source.height() + padding.height()),
35                      1.0f,
36                      false));
37  result->DrawImageInt(
38      gfx::ImageSkia::CreateFrom1xBitmap(source),
39      0, 0, source.width(), source.height(),
40      padding.left(), padding.top(), source.width(), source.height(),
41      false);
42  return result->ExtractImageRep().sk_bitmap();
43}
44
45}  // namespace
46
47ExtensionIconManager::ExtensionIconManager()
48    : monochrome_(false),
49      weak_ptr_factory_(this)  {
50}
51
52ExtensionIconManager::~ExtensionIconManager() {
53}
54
55void ExtensionIconManager::LoadIcon(content::BrowserContext* context,
56                                    const extensions::Extension* extension) {
57  extensions::ExtensionResource icon_resource =
58      extensions::IconsInfo::GetIconResource(
59          extension,
60          extension_misc::EXTENSION_ICON_BITTY,
61          ExtensionIconSet::MATCH_BIGGER);
62  if (!icon_resource.extension_root().empty()) {
63    // Insert into pending_icons_ first because LoadImage can call us back
64    // synchronously if the image is already cached.
65    pending_icons_.insert(extension->id());
66    extensions::ImageLoader* loader = extensions::ImageLoader::Get(context);
67    loader->LoadImageAsync(extension, icon_resource,
68                           gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize),
69                           base::Bind(
70                               &ExtensionIconManager::OnImageLoaded,
71                               weak_ptr_factory_.GetWeakPtr(),
72                               extension->id()));
73  }
74}
75
76const SkBitmap& ExtensionIconManager::GetIcon(const std::string& extension_id) {
77  const SkBitmap* result = NULL;
78  if (ContainsKey(icons_, extension_id)) {
79    result = &icons_[extension_id];
80  } else {
81    EnsureDefaultIcon();
82    result = &default_icon_;
83  }
84  DCHECK(result);
85  DCHECK_EQ(gfx::kFaviconSize + padding_.width(), result->width());
86  DCHECK_EQ(gfx::kFaviconSize + padding_.height(), result->height());
87  return *result;
88}
89
90void ExtensionIconManager::RemoveIcon(const std::string& extension_id) {
91  icons_.erase(extension_id);
92  pending_icons_.erase(extension_id);
93}
94
95void ExtensionIconManager::OnImageLoaded(const std::string& extension_id,
96                                         const gfx::Image& image) {
97  if (image.IsEmpty())
98    return;
99
100  // We may have removed the icon while waiting for it to load. In that case,
101  // do nothing.
102  if (!ContainsKey(pending_icons_, extension_id))
103    return;
104
105  pending_icons_.erase(extension_id);
106  icons_[extension_id] = ApplyTransforms(*image.ToSkBitmap());
107}
108
109void ExtensionIconManager::EnsureDefaultIcon() {
110  if (default_icon_.empty()) {
111    ResourceBundle& rb = ResourceBundle::GetSharedInstance();
112    SkBitmap src = rb.GetImageNamed(IDR_EXTENSIONS_SECTION).AsBitmap();
113    default_icon_ = ApplyTransforms(src);
114  }
115}
116
117SkBitmap ExtensionIconManager::ApplyTransforms(const SkBitmap& source) {
118  SkBitmap result = source;
119
120  if (result.width() != gfx::kFaviconSize ||
121      result.height() != gfx::kFaviconSize) {
122    result = skia::ImageOperations::Resize(
123        result, skia::ImageOperations::RESIZE_LANCZOS3,
124        gfx::kFaviconSize, gfx::kFaviconSize);
125  }
126
127  if (monochrome_) {
128    color_utils::HSL shift = {-1, 0, 0.6};
129    result = SkBitmapOperations::CreateHSLShiftedBitmap(result, shift);
130  }
131
132  if (!padding_.empty())
133    result = ApplyPadding(result, padding_);
134
135  return result;
136}
137