// 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 #include #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 { 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(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; }