Chromium Code Reviews| Index: chrome/common/resource_bundle_linux.cc |
| diff --git a/chrome/common/resource_bundle_linux.cc b/chrome/common/resource_bundle_linux.cc |
| index 870d81753a176f80ad5a0991e1509b5e9be18ba4..57cce50528dbf15ecd4c86ee788bb8f47b0fae24 100644 |
| --- a/chrome/common/resource_bundle_linux.cc |
| +++ b/chrome/common/resource_bundle_linux.cc |
| @@ -12,6 +12,7 @@ |
| #include "base/data_pack.h" |
| #include "base/file_path.h" |
| #include "base/file_util.h" |
| +#include "base/gfx/gtk_util.h" |
| #include "base/logging.h" |
| #include "base/path_service.h" |
| #include "base/string_piece.h" |
| @@ -19,9 +20,48 @@ |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/gfx/chrome_font.h" |
| #include "chrome/common/l10n_util.h" |
| +#include "SkBitmap.h" |
| + |
| +namespace { |
| + |
| +#if defined(TOOLKIT_GTK) |
| +// Convert the raw image data into a GdkPixbuf. The GdkPixbuf that is returned |
| +// has a ref count of 1 so the caller must call g_object_unref to free the |
| +// memory. |
| +GdkPixbuf* LoadPixbuf(std::vector<unsigned char>& data) { |
| + GdkPixbufLoader* loader = gdk_pixbuf_loader_new(); |
| + bool ok = gdk_pixbuf_loader_write(loader, static_cast<guint8*>(data.data()), |
| + data.size(), NULL); |
| + if (!ok) |
| + return NULL; |
|
Dean McNamee
2009/04/15 19:58:17
we'll leak the loader, but I guess I'm ok w/ that.
|
| + // Calling gdk_pixbuf_loader_close forces the data to be parsed by the |
| + // loader. We must do this before calling gdk_pixbuf_loader_get_pixbuf. |
| + ok = gdk_pixbuf_loader_close(loader, NULL); |
| + if (!ok) |
| + return NULL; |
| + GdkPixbuf* pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); |
| + if (!pixbuf) |
| + return NULL; |
| + |
| + // The pixbuf is owned by the loader, so add a ref so when we delete the |
| + // loader, the pixbuf still exists. |
| + g_object_ref(pixbuf); |
| + g_object_unref(loader); |
| + |
| + return pixbuf; |
| +} |
| +#endif |
| + |
| +} // namespace |
| ResourceBundle::~ResourceBundle() { |
| FreeImages(); |
| + // Free GdkPixbufs. |
| + for (GdkPixbufMap::iterator i = gdk_pixbufs_.begin(); |
| + i != gdk_pixbufs_.end(); i++) { |
| + g_object_unref(i->second); |
| + } |
| + gdk_pixbufs_.clear(); |
| delete locale_resources_data_; |
| locale_resources_data_ = NULL; |
| @@ -123,27 +163,52 @@ string16 ResourceBundle::GetLocalizedString(int message_id) { |
| } |
| #if defined(TOOLKIT_GTK) |
| -GdkPixbuf* ResourceBundle::LoadPixbuf(int resource_id) { |
| - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
| +GdkPixbuf* ResourceBundle::GetPixbufNamed(int resource_id) { |
| + // Check to see if we already have the pixbuf in the cache. |
| + { |
| + AutoLock lock_scope(lock_); |
| + GdkPixbufMap::const_iterator found = gdk_pixbufs_.find(resource_id); |
| + if (found != gdk_pixbufs_.end()) |
| + return found->second; |
| + } |
| + |
| + |
| std::vector<unsigned char> data; |
| - rb.LoadImageResourceBytes(resource_id, &data); |
| + LoadImageResourceBytes(resource_id, &data); |
| + GdkPixbuf* pixbuf = LoadPixbuf(data); |
| - GdkPixbufLoader* loader = gdk_pixbuf_loader_new(); |
| - bool ok = gdk_pixbuf_loader_write(loader, static_cast<guint8*>(data.data()), |
| - data.size(), NULL); |
| - DCHECK(ok) << "failed to write " << resource_id; |
| - // Calling gdk_pixbuf_loader_close forces the data to be parsed by the |
| - // loader. We must do this before calling gdk_pixbuf_loader_get_pixbuf. |
| - ok = gdk_pixbuf_loader_close(loader, NULL); |
| - DCHECK(ok) << "close failed " << resource_id; |
| - GdkPixbuf* pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); |
| - DCHECK(pixbuf) << "failed to load " << resource_id << " " << data.size(); |
| + // We loaded successfully. Cache the pixbuf. |
| + if (pixbuf) { |
| + AutoLock lock_scope(lock_); |
| - // The pixbuf is owned by the loader, so add a ref so when we delete the |
| - // loader, the pixbuf still exists. |
| - g_object_ref(pixbuf); |
| - g_object_unref(loader); |
| + // Another thread raced us, and has already cached the pixbuf. |
| + if (gdk_pixbufs_.count(resource_id)) { |
| + g_object_unref(pixbuf); |
| + return gdk_pixbufs_[resource_id]; |
| + } |
| - return pixbuf; |
| + gdk_pixbufs_[resource_id] = pixbuf; |
| + return pixbuf; |
| + } |
| + |
| + // We failed to retrieve the bitmap, show a debugging red square. |
| + { |
| + LOG(WARNING) << "Unable to load GdkPixbuf with id " << resource_id; |
| + NOTREACHED(); // Want to assert in debug mode. |
| + |
| + AutoLock lock_scope(lock_); // Guard empty_bitmap initialization. |
| + |
| + static GdkPixbuf* empty_bitmap = NULL; |
| + if (!empty_bitmap) { |
| + // The placeholder bitmap is bright red so people notice the problem. |
| + // This bitmap will be leaked, but this code should never be hit. |
| + scoped_ptr<SkBitmap> skia_bitmap(new SkBitmap()); |
| + skia_bitmap->setConfig(SkBitmap::kARGB_8888_Config, 32, 32); |
| + skia_bitmap->allocPixels(); |
| + skia_bitmap->eraseARGB(255, 255, 0, 0); |
| + empty_bitmap = gfx::GdkPixbufFromSkBitmap(skia_bitmap.get()); |
| + } |
| + return empty_bitmap; |
| + } |
| } |
| #endif |