| // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/icon_loader.h" |
| |
| #include <windows.h> |
| #include <shellapi.h> |
| |
| #include "base/file_util.h" |
| #include "base/gfx/size.h" |
| #include "base/message_loop.h" |
| #include "base/ref_counted.h" |
| #include "base/string_util.h" |
| #include "base/task.h" |
| #include "base/thread.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/common/gfx/icon_util.h" |
| |
| #include "SkBitmap.h" |
| |
| namespace { |
| class IconLoaderProcessor : |
| public base::RefCountedThreadSafe<IconLoaderProcessor> { |
| public: |
| explicit IconLoaderProcessor(IconLoader* target) |
| : target_(target), |
| bitmap_(NULL), |
| small_icon_(NULL), |
| large_icon_(NULL), |
| loading_from_resource_(target->loading_from_resource_), |
| icon_type_(target->icon_type_), |
| icon_size_(target->icon_size_) { |
| DCHECK(target); |
| path_ = target->path_; |
| target_message_loop_ = MessageLoop::current(); |
| } |
| |
| virtual ~IconLoaderProcessor() { |
| delete bitmap_; |
| if (small_icon_) |
| ::DestroyIcon(small_icon_); |
| if (large_icon_) { |
| ::DestroyIcon(large_icon_); |
| } |
| } |
| |
| // Loads the icon with the specified dimensions. |
| HICON LoadSizedIcon(int width, int height) { |
| return static_cast<HICON>(LoadImage(NULL, |
| path_.c_str(), |
| width, height, |
| IMAGE_ICON, |
| LR_LOADTRANSPARENT | LR_LOADFROMFILE)); |
| } |
| |
| // Invoked from the original thread. |
| void Cancel() { |
| target_ = NULL; |
| } |
| |
| // Invoked in the file thread. Never access target_ from this method. |
| void ReadIcon() { |
| if (loading_from_resource_) |
| ReadIconFromFileResource(); |
| else |
| ReadIconFile(); |
| |
| target_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( |
| this, &IconLoaderProcessor::NotifyFetcher)); |
| } |
| |
| // Invoked on the file thread to read a normal .ico file. |
| void ReadIconFile() { |
| // We start by loading the same icon image in the two desired dimensions, |
| // based on the dimensions we get back when we query the system. |
| int small_width = ::GetSystemMetrics(SM_CXSMICON); |
| int small_height = ::GetSystemMetrics(SM_CYSMICON); |
| int large_width = ::GetSystemMetrics(SM_CXICON); |
| int large_height = ::GetSystemMetrics(SM_CYICON); |
| small_icon_ = LoadSizedIcon(small_width, small_height); |
| large_icon_ = LoadSizedIcon(large_width, large_height); |
| |
| // TODO(idana): Bug 991356. Currently, if the client asks for the icon in |
| // the form of an SkBitmap object, then we try converting the large icon |
| // into an SkBitmap, if the icon was successfully loaded. If the large icon |
| // is not available, we convert the small icon. The problem with converting |
| // the small or the large icon is that the resulting image is going to have |
| // the same dimensions as the icon (for example, 32X32 pixels). This can be |
| // problematic if, for example, the client tries to display the resulting |
| // image as a thumbnail. This will result in the client streching the |
| // bitmap from 32X32 to 128X128 which will decrease the image's quality. |
| // |
| // Since it is possible for web applications to define large .PNG images as |
| // their icons, the resulting .ico files created for these web applications |
| // contain icon images in sizes much larger the the system default sizes |
| // for icons. We can solve the aforementioned limitation by allowing the |
| // client to specify the size of the resulting image, when requesting an |
| // SkBitmap. The IconLoader code can then load a larger icon from the .ico |
| // file. |
| // |
| // Note that currently the components in Chrome that deal with SkBitmaps |
| // that represent application icons use the images to display icon size |
| // images and therefore the limitation above doesn't really manifest |
| // itself. |
| HICON icon_to_convert = NULL; |
| gfx::Size s; |
| if (icon_type_ == IconLoader::SK_BITMAP) { |
| if (large_icon_) { |
| icon_to_convert = large_icon_; |
| s.SetSize(large_width, large_height); |
| } else if (small_icon_) { |
| icon_to_convert = small_icon_; |
| s.SetSize(small_width, small_height); |
| } |
| } |
| |
| if (icon_to_convert) { |
| bitmap_ = IconUtil::CreateSkBitmapFromHICON(icon_to_convert, s); |
| DCHECK(bitmap_); |
| if (small_icon_) |
| ::DestroyIcon(small_icon_); |
| if (large_icon_) |
| ::DestroyIcon(large_icon_); |
| |
| small_icon_ = NULL; |
| large_icon_ = NULL; |
| } |
| } |
| |
| void ReadIconFromFileResource() { |
| int size = 0; |
| switch (icon_size_) { |
| case IconLoader::SMALL: |
| size = SHGFI_SMALLICON; |
| break; |
| case IconLoader::NORMAL: |
| size = 0; |
| break; |
| case IconLoader::LARGE: |
| size = SHGFI_LARGEICON; |
| break; |
| default: |
| NOTREACHED(); |
| } |
| SHFILEINFO file_info = { 0 }; |
| if (!SHGetFileInfo(path_.c_str(), FILE_ATTRIBUTE_NORMAL, &file_info, |
| sizeof(SHFILEINFO), |
| SHGFI_ICON | size | SHGFI_USEFILEATTRIBUTES)) |
| return; |
| |
| ICONINFO icon_info = { 0 }; |
| BITMAP bitmap_info = { 0 }; |
| |
| BOOL r = ::GetIconInfo(file_info.hIcon, &icon_info); |
| DCHECK(r); |
| r = ::GetObject(icon_info.hbmMask, sizeof(bitmap_info), &bitmap_info); |
| DCHECK(r); |
| |
| gfx::Size icon_size(bitmap_info.bmWidth, bitmap_info.bmHeight); |
| bitmap_ = IconUtil::CreateSkBitmapFromHICON(file_info.hIcon, icon_size); |
| } |
| |
| // Invoked in the target thread. |
| void NotifyFetcher() { |
| if (target_) { |
| switch (target_->icon_type_) { |
| case IconLoader::SK_BITMAP: |
| if (target_->OnLoadComplete(bitmap_)) { |
| // Receiver took ownership of the bitmap. |
| bitmap_ = NULL; |
| } |
| break; |
| case IconLoader::WINDOWS_HICON: |
| if (target_->OnLoadComplete(small_icon_, large_icon_)) { |
| // Receiver took ownership of the icons. |
| small_icon_ = NULL; |
| large_icon_ = NULL; |
| } |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| } |
| |
| private: |
| // The message loop object of the thread in which we notify the delegate. |
| MessageLoop* target_message_loop_; |
| |
| // The corresponding file fetcher or NULL if this task has been canceled. |
| IconLoader* target_; |
| |
| // The path of the file. |
| std::wstring path_; |
| |
| // Fields from IconLoader that we need to copy as we cannot access them |
| // directly from the target_ in a thread-safe way. |
| bool loading_from_resource_; |
| IconLoader::IconSize icon_size_; |
| IconLoader::IconType icon_type_; |
| |
| // The result bitmap. |
| SkBitmap* bitmap_; |
| |
| // The result small icon. |
| HICON small_icon_; |
| |
| // The result large icon. |
| HICON large_icon_; |
| |
| DISALLOW_EVIL_CONSTRUCTORS(IconLoaderProcessor); |
| }; |
| } |
| |
| // static |
| IconLoader* IconLoader::CreateIconLoaderForFile(const std::wstring& path, |
| IconType icon_type, |
| Delegate* delegate) { |
| // Note: the icon size is unused in this case. |
| return new IconLoader(path, icon_type, false, IconLoader::NORMAL, delegate); |
| } |
| |
| // static |
| IconLoader* IconLoader::CreateIconLoaderForFileResource( |
| const std::wstring& path, IconSize size, Delegate* delegate) { |
| return new IconLoader(path, IconLoader::SK_BITMAP, true, size, delegate); |
| } |
| |
| IconLoader::IconLoader(const std::wstring& path, |
| IconType type, |
| bool from_resource, |
| IconSize size, |
| Delegate* delegate) |
| : path_(path), |
| icon_type_(type), |
| loading_from_resource_(from_resource), |
| icon_size_(size), |
| delegate_(delegate), |
| processor_(NULL) { |
| DCHECK(delegate_); |
| } |
| |
| IconLoader::~IconLoader() { |
| Cancel(); |
| } |
| |
| void IconLoader::Start() { |
| processor_ = new IconLoaderProcessor(this); |
| processor_->AddRef(); |
| g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE, |
| NewRunnableMethod(processor_, &IconLoaderProcessor::ReadIcon)); |
| } |
| |
| void IconLoader::Cancel() { |
| if (processor_) { |
| processor_->Cancel(); |
| processor_->Release(); |
| processor_ = NULL; |
| } |
| delegate_ = NULL; |
| } |
| |
| bool IconLoader::OnLoadComplete(SkBitmap* bitmap) { |
| if (delegate_) { |
| return delegate_->OnSkBitmapLoaded(this, bitmap); |
| // We are likely deleted after this point. |
| } |
| return false; |
| } |
| |
| bool IconLoader::OnLoadComplete(HICON small_icon, HICON large_icon) { |
| if (delegate_) { |
| return delegate_->OnHICONLoaded(this, small_icon, large_icon); |
| // We are likely deleted after this point. |
| } |
| return false; |
| } |
| |