summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorrsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-11 17:42:28 +0000
committerrsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-11 17:42:28 +0000
commit40a080e65b4205fc5f75dbbe43ca033a5a9664bd (patch)
tree188eb7f12d1dcd17238507d307539d18f52c6448 /ui
parentd98dfa02191fbe47c1104b1129293b29f6600e87 (diff)
downloadchromium_src-40a080e65b4205fc5f75dbbe43ca033a5a9664bd.zip
chromium_src-40a080e65b4205fc5f75dbbe43ca033a5a9664bd.tar.gz
chromium_src-40a080e65b4205fc5f75dbbe43ca033a5a9664bd.tar.bz2
Add ui::gfx::Image to eventually replace gfx::ScopedImage.
This achieves the same goal as ScopedImage, namely encapsulating the memory management of any image type. But ui::gfx::Image goes further by providing conversion helpers between different image types. BUG=carnitas TEST=gfx_unittests and unit_tests Review URL: http://codereview.chromium.org/6312159 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@74624 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r--ui/gfx/gfx.gyp5
-rw-r--r--ui/gfx/image.cc281
-rw-r--r--ui/gfx/image.h94
-rw-r--r--ui/gfx/image_mac.mm20
-rw-r--r--ui/gfx/image_unittest.cc109
-rw-r--r--ui/gfx/image_unittest.h59
6 files changed, 568 insertions, 0 deletions
diff --git a/ui/gfx/gfx.gyp b/ui/gfx/gfx.gyp
index 04df70d..12d368d 100644
--- a/ui/gfx/gfx.gyp
+++ b/ui/gfx/gfx.gyp
@@ -28,6 +28,8 @@
'codec/png_codec_unittest.cc',
'color_utils_unittest.cc',
'font_unittest.cc',
+ 'image_unittest.cc',
+ 'image_unittest.h',
'insets_unittest.cc',
'rect_unittest.cc',
'run_all_unittests.cc',
@@ -112,6 +114,9 @@
'gfx_paths.h',
'gfx_module.cc',
'gfx_module.h',
+ 'image.cc',
+ 'image.h',
+ 'image_mac.mm',
'insets.cc',
'insets.h',
'native_widget_types.h',
diff --git a/ui/gfx/image.cc b/ui/gfx/image.cc
new file mode 100644
index 0000000..6eac4c0
--- /dev/null
+++ b/ui/gfx/image.cc
@@ -0,0 +1,281 @@
+// Copyright (c) 2011 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 "ui/gfx/image.h"
+
+#include "base/logging.h"
+
+#if defined(OS_LINUX)
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <glib-object.h>
+#include "ui/gfx/canvas_skia.h"
+#include "ui/gfx/gtk_util.h"
+#elif defined(OS_MACOSX)
+#include "base/mac/mac_util.h"
+#include "skia/ext/skia_utils_mac.h"
+#endif
+
+namespace ui {
+namespace gfx {
+
+namespace internal {
+
+#if defined(OS_MACOSX)
+// This is a wrapper around gfx::NSImageToSkBitmap() because this cross-platform
+// file cannot include the [square brackets] of ObjC.
+const SkBitmap* NSImageToSkBitmap(NSImage* image);
+#endif
+
+#if defined(OS_LINUX)
+const SkBitmap* GdkPixbufToSkBitmap(GdkPixbuf* pixbuf) {
+ ::gfx::CanvasSkia canvas(gdk_pixbuf_get_width(pixbuf),
+ gdk_pixbuf_get_height(pixbuf),
+ false);
+ canvas.DrawGdkPixbuf(pixbuf, 0, 0);
+ return new SkBitmap(canvas.ExtractBitmap());
+}
+#endif
+
+class SkBitmapRep;
+class GdkPixbufRep;
+class NSImageRep;
+
+// An ImageRep is the object that holds the backing memory for an Image. Each
+// RepresentationType has an ImageRep subclass that is responsible for freeing
+// the memory that the ImageRep holds. When an ImageRep is created, it expects
+// to take ownership of the image, without having to retain it or increase its
+// reference count.
+class ImageRep {
+ public:
+ explicit ImageRep(Image::RepresentationType rep) : type_(rep) {}
+
+ // Deletes the associated pixels of an ImageRep.
+ virtual ~ImageRep() {}
+
+ // Cast helpers ("fake RTTI").
+ SkBitmapRep* AsSkBitmapRep() {
+ CHECK_EQ(type_, Image::kSkBitmapRep);
+ return reinterpret_cast<SkBitmapRep*>(this);
+ }
+
+#if defined(OS_LINUX)
+ GdkPixbufRep* AsGdkPixbufRep() {
+ CHECK_EQ(type_, Image::kGdkPixbufRep);
+ return reinterpret_cast<GdkPixbufRep*>(this);
+ }
+#endif
+
+#if defined(OS_MACOSX)
+ NSImageRep* AsNSImageRep() {
+ CHECK_EQ(type_, Image::kNSImageRep);
+ return reinterpret_cast<NSImageRep*>(this);
+ }
+#endif
+
+ Image::RepresentationType type() const { return type_; }
+
+ private:
+ Image::RepresentationType type_;
+};
+
+class SkBitmapRep : public ImageRep {
+ public:
+ explicit SkBitmapRep(const SkBitmap* bitmap)
+ : ImageRep(Image::kSkBitmapRep),
+ bitmap_(bitmap) {
+ CHECK(bitmap);
+ }
+
+ virtual ~SkBitmapRep() {
+ delete bitmap_;
+ bitmap_ = NULL;
+ }
+
+ const SkBitmap* bitmap() const { return bitmap_; }
+
+ private:
+ const SkBitmap* bitmap_;
+
+ DISALLOW_COPY_AND_ASSIGN(SkBitmapRep);
+};
+
+#if defined(OS_LINUX)
+class GdkPixbufRep : public ImageRep {
+ public:
+ explicit GdkPixbufRep(GdkPixbuf* pixbuf)
+ : ImageRep(Image::kGdkPixbufRep),
+ pixbuf_(pixbuf) {
+ CHECK(pixbuf);
+ }
+
+ virtual ~GdkPixbufRep() {
+ if (pixbuf_) {
+ g_object_unref(pixbuf_);
+ pixbuf_ = NULL;
+ }
+ }
+
+ GdkPixbuf* pixbuf() const { return pixbuf_; }
+
+ private:
+ GdkPixbuf* pixbuf_;
+
+ DISALLOW_COPY_AND_ASSIGN(GdkPixbufRep);
+};
+#endif
+
+#if defined(OS_MACOSX)
+class NSImageRep : public ImageRep {
+ public:
+ explicit NSImageRep(NSImage* image)
+ : ImageRep(Image::kNSImageRep),
+ image_(image) {
+ CHECK(image);
+ }
+
+ virtual ~NSImageRep() {
+ base::mac::NSObjectRelease(image_);
+ image_ = nil;
+ }
+
+ NSImage* image() const { return image_; }
+
+ private:
+ NSImage* image_;
+
+ DISALLOW_COPY_AND_ASSIGN(NSImageRep);
+};
+#endif
+
+} // namespace internal
+
+Image::Image(const SkBitmap* bitmap)
+ : default_representation_(Image::kSkBitmapRep) {
+ internal::SkBitmapRep* rep = new internal::SkBitmapRep(bitmap);
+ AddRepresentation(rep);
+}
+
+#if defined(OS_LINUX)
+Image::Image(GdkPixbuf* pixbuf)
+ : default_representation_(Image::kGdkPixbufRep) {
+ internal::GdkPixbufRep* rep = new internal::GdkPixbufRep(pixbuf);
+ AddRepresentation(rep);
+}
+#endif
+
+#if defined(OS_MACOSX)
+Image::Image(NSImage* image) : default_representation_(Image::kNSImageRep) {
+ internal::NSImageRep* rep = new internal::NSImageRep(image);
+ AddRepresentation(rep);
+}
+#endif
+
+Image::~Image() {
+ for (RepresentationMap::iterator it = representations_.begin();
+ it != representations_.end(); ++it) {
+ delete it->second;
+ }
+ representations_.clear();
+}
+
+Image::operator const SkBitmap*() {
+ internal::ImageRep* rep = GetRepresentation(Image::kSkBitmapRep);
+ return rep->AsSkBitmapRep()->bitmap();
+}
+
+Image::operator const SkBitmap&() {
+ return *(this->operator const SkBitmap*());
+}
+
+#if defined(OS_LINUX)
+Image::operator GdkPixbuf*() {
+ internal::ImageRep* rep = GetRepresentation(Image::kGdkPixbufRep);
+ return rep->AsGdkPixbufRep()->pixbuf();
+}
+#endif
+
+#if defined(OS_MACOSX)
+Image::operator NSImage*() {
+ internal::ImageRep* rep = GetRepresentation(Image::kNSImageRep);
+ return rep->AsNSImageRep()->image();
+}
+#endif
+
+internal::ImageRep* Image::DefaultRepresentation() {
+ RepresentationMap::iterator it =
+ representations_.find(default_representation_);
+ DCHECK(it != representations_.end());
+ return it->second;
+}
+
+internal::ImageRep* Image::GetRepresentation(RepresentationType rep_type) {
+ // If the requested rep is the default, return it.
+ internal::ImageRep* default_rep = DefaultRepresentation();
+ if (rep_type == default_representation_)
+ return default_rep;
+
+ // Check to see if the representation already exists.
+ RepresentationMap::iterator it = representations_.find(rep_type);
+ if (it != representations_.end())
+ return it->second;
+
+ // At this point, the requested rep does not exist, so it must be converted
+ // from the default rep.
+
+ // Handle native-to-Skia conversion.
+ if (rep_type == Image::kSkBitmapRep) {
+ internal::SkBitmapRep* rep = NULL;
+#if defined(OS_LINUX)
+ if (default_representation_ == Image::kGdkPixbufRep) {
+ internal::GdkPixbufRep* pixbuf_rep = default_rep->AsGdkPixbufRep();
+ rep = new internal::SkBitmapRep(
+ internal::GdkPixbufToSkBitmap(pixbuf_rep->pixbuf()));
+ }
+#elif defined(OS_MACOSX)
+ if (default_representation_ == Image::kNSImageRep) {
+ internal::NSImageRep* nsimage_rep = default_rep->AsNSImageRep();
+ rep = new internal::SkBitmapRep(
+ internal::NSImageToSkBitmap(nsimage_rep->image()));
+ }
+#endif
+ if (rep) {
+ AddRepresentation(rep);
+ return rep;
+ }
+ NOTREACHED();
+ }
+
+ // Handle Skia-to-native conversions.
+ if (default_rep->type() == Image::kSkBitmapRep) {
+ internal::SkBitmapRep* skia_rep = default_rep->AsSkBitmapRep();
+ internal::ImageRep* native_rep = NULL;
+#if defined(OS_LINUX)
+ if (rep_type == Image::kGdkPixbufRep) {
+ GdkPixbuf* pixbuf = ::gfx::GdkPixbufFromSkBitmap(skia_rep->bitmap());
+ native_rep = new internal::GdkPixbufRep(pixbuf);
+ }
+#elif defined(OS_MACOSX)
+ if (rep_type == Image::kNSImageRep) {
+ NSImage* image = ::gfx::SkBitmapToNSImage(*(skia_rep->bitmap()));
+ base::mac::NSObjectRetain(image);
+ native_rep = new internal::NSImageRep(image);
+ }
+#endif
+ if (native_rep) {
+ AddRepresentation(native_rep);
+ return native_rep;
+ }
+ NOTREACHED();
+ }
+
+ // Something went seriously wrong...
+ return NULL;
+}
+
+void Image::AddRepresentation(internal::ImageRep* rep) {
+ representations_.insert(std::make_pair(rep->type(), rep));
+}
+
+} // namespace gfx
+} // namespace ui
diff --git a/ui/gfx/image.h b/ui/gfx/image.h
new file mode 100644
index 0000000..7abede8
--- /dev/null
+++ b/ui/gfx/image.h
@@ -0,0 +1,94 @@
+// Copyright (c) 2011 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.
+
+#ifndef UI_GFX_IMAGE_H_
+#define UI_GFX_IMAGE_H_
+#pragma once
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "build/build_config.h"
+#include "gfx/native_widget_types.h" // Forward-declares GdkPixbuf and NSImage.
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace {
+class ImageTest;
+}
+
+namespace ui {
+namespace gfx {
+
+namespace internal {
+class ImageRep;
+}
+
+// An Image wraps an image any flavor, be it platform-native GdkBitmap/NSImage,
+// or a SkBitmap. This also provides easy conversion to other image types
+// through operator overloading. It will cache the converted representations
+// internally to prevent double-conversion.
+//
+// The lifetime of both the initial representation and any converted ones are
+// tied to the lifetime of the Image object itself.
+class Image {
+ public:
+ enum RepresentationType {
+ kGdkPixbufRep,
+ kNSImageRep,
+ kSkBitmapRep,
+ };
+
+ // Creates a new image with the default representation. The object will take
+ // ownership of the image.
+ explicit Image(const SkBitmap* bitmap);
+#if defined(OS_LINUX)
+ // Does not increase |pixbuf|'s reference count.
+ explicit Image(GdkPixbuf* pixbuf);
+#elif defined(OS_MACOSX)
+ // Does not retain |image|.
+ explicit Image(NSImage* image);
+#endif
+
+ // Deletes the image and all of its cached representations.
+ ~Image();
+
+ // Conversion handlers.
+ operator const SkBitmap*();
+ operator const SkBitmap&();
+#if defined(OS_LINUX)
+ operator GdkPixbuf*();
+#elif defined(OS_MACOSX)
+ operator NSImage*();
+#endif
+
+ private:
+ friend class ::ImageTest;
+
+ // Returns the ImageRep for the default representation.
+ internal::ImageRep* DefaultRepresentation();
+
+ // Returns a ImageRep for the given representation type, converting and
+ // caching if necessary.
+ internal::ImageRep* GetRepresentation(RepresentationType rep);
+
+ // Stores a representation into the map.
+ void AddRepresentation(internal::ImageRep* rep);
+
+ // The type of image that was passed to the constructor. This key will always
+ // exist in the |representations_| map.
+ RepresentationType default_representation_;
+
+ typedef std::map<RepresentationType, internal::ImageRep*> RepresentationMap;
+ // All the representations of an Image. Size will always be at least one, with
+ // more for any converted representations.
+ RepresentationMap representations_;
+
+ DISALLOW_COPY_AND_ASSIGN(Image);
+};
+
+} // namespace gfx
+} // namespace ui
+
+#endif // UI_GFX_IMAGE_H_
diff --git a/ui/gfx/image_mac.mm b/ui/gfx/image_mac.mm
new file mode 100644
index 0000000..c74d52d
--- /dev/null
+++ b/ui/gfx/image_mac.mm
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 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.
+
+#import <AppKit/AppKit.h>
+
+#include "skia/ext/skia_utils_mac.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace ui {
+namespace gfx {
+namespace internal {
+
+const SkBitmap* NSImageToSkBitmap(NSImage* image) {
+ return new SkBitmap(::gfx::NSImageToSkBitmap(image, [image size], false));
+}
+
+} // namespace internal
+} // namespace gfx
+} // namespace ui
diff --git a/ui/gfx/image_unittest.cc b/ui/gfx/image_unittest.cc
new file mode 100644
index 0000000..40e8bc80
--- /dev/null
+++ b/ui/gfx/image_unittest.cc
@@ -0,0 +1,109 @@
+// Copyright (c) 2011 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 "ui/gfx/image.h"
+
+#include "base/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/image_unittest.h"
+
+#if defined(OS_LINUX)
+#include <gtk/gtk.h>
+#include "gfx/gtk_util.h"
+#elif defined(OS_MACOSX)
+#include "base/mac/mac_util.h"
+#include "skia/ext/skia_utils_mac.h"
+#endif
+
+namespace {
+
+using namespace ui::gfx::test;
+
+#if defined(TOOLKIT_VIEWS)
+const bool kUsesSkiaNatively = true;
+#else
+const bool kUsesSkiaNatively = false;
+#endif
+
+class ImageTest : public testing::Test {
+ public:
+ size_t GetRepCount(const ui::gfx::Image& image) {
+ return image.representations_.size();
+ }
+};
+
+TEST_F(ImageTest, SkiaToSkia) {
+ ui::gfx::Image image(CreateBitmap());
+ const SkBitmap* bitmap = static_cast<const SkBitmap*>(image);
+ EXPECT_TRUE(bitmap);
+ EXPECT_FALSE(bitmap->isNull());
+ EXPECT_EQ(1U, GetRepCount(image));
+
+ // Make sure double conversion doesn't happen.
+ bitmap = static_cast<const SkBitmap*>(image);
+ EXPECT_TRUE(bitmap);
+ EXPECT_FALSE(bitmap->isNull());
+ EXPECT_EQ(1U, GetRepCount(image));
+}
+
+TEST_F(ImageTest, SkiaToSkiaRef) {
+ ui::gfx::Image image(CreateBitmap());
+
+ const SkBitmap& bitmap = static_cast<const SkBitmap&>(image);
+ EXPECT_FALSE(bitmap.isNull());
+ EXPECT_EQ(1U, GetRepCount(image));
+
+ const SkBitmap* bitmap1 = static_cast<const SkBitmap*>(image);
+ EXPECT_FALSE(bitmap1->isNull());
+ EXPECT_EQ(1U, GetRepCount(image));
+}
+
+TEST_F(ImageTest, SkiaToPlatform) {
+ ui::gfx::Image image(CreateBitmap());
+ const size_t kRepCount = kUsesSkiaNatively ? 1U : 2U;
+
+ EXPECT_TRUE(static_cast<PlatformImage>(image));
+ EXPECT_EQ(kRepCount, GetRepCount(image));
+
+ const SkBitmap& bitmap = static_cast<const SkBitmap&>(image);
+ EXPECT_FALSE(bitmap.isNull());
+ EXPECT_EQ(kRepCount, GetRepCount(image));
+}
+
+TEST_F(ImageTest, PlatformToSkia) {
+ ui::gfx::Image image(CreatePlatformImage());
+ const size_t kRepCount = kUsesSkiaNatively ? 1U : 2U;
+
+ const SkBitmap* bitmap = static_cast<const SkBitmap*>(image);
+ EXPECT_TRUE(bitmap);
+ EXPECT_FALSE(bitmap->isNull());
+ EXPECT_EQ(kRepCount, GetRepCount(image));
+
+ EXPECT_TRUE(static_cast<PlatformImage>(image));
+ EXPECT_EQ(kRepCount, GetRepCount(image));
+}
+
+TEST_F(ImageTest, PlatformToPlatform) {
+ ui::gfx::Image image(CreatePlatformImage());
+ EXPECT_TRUE(static_cast<PlatformImage>(image));
+ EXPECT_EQ(1U, GetRepCount(image));
+
+ // Make sure double conversion doesn't happen.
+ EXPECT_TRUE(static_cast<PlatformImage>(image));
+ EXPECT_EQ(1U, GetRepCount(image));
+}
+
+TEST_F(ImageTest, CheckSkiaColor) {
+ ui::gfx::Image image(CreatePlatformImage());
+ const SkBitmap& bitmap(image);
+ uint32_t* pixel = bitmap.getAddr32(10, 10);
+ EXPECT_EQ(SK_ColorRED, *pixel);
+}
+
+// Integration tests with UI toolkit frameworks require linking against the
+// Views library and cannot be here (gfx_unittests doesn't include it). They
+// instead live in /chrome/browser/ui/tests/ui_gfx_image_unittest.cc.
+
+} // namespace
diff --git a/ui/gfx/image_unittest.h b/ui/gfx/image_unittest.h
new file mode 100644
index 0000000..4324caa
--- /dev/null
+++ b/ui/gfx/image_unittest.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2011 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.
+
+// Because the unit tests for ui::gfx::Image are spread across multiple
+// implementation files, this header contains the reusable components.
+
+#ifndef UI_GFX_IMAGE_UNITTEST_H_
+#define UI_GFX_IMAGE_UNITTEST_H_
+
+#include "base/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+#if defined(OS_LINUX)
+#include "gfx/gtk_util.h"
+#elif defined(OS_MACOSX)
+#include "base/mac/mac_util.h"
+#include "skia/ext/skia_utils_mac.h"
+#endif
+
+namespace ui {
+namespace gfx {
+namespace test {
+
+#if defined(OS_MACOSX)
+typedef NSImage* PlatformImage;
+#elif defined(OS_LINUX) && !defined(TOOLKIT_VIEWS)
+typedef GdkPixbuf* PlatformImage;
+#else
+typedef const SkBitmap* PlatformImage;
+#endif
+
+SkBitmap* CreateBitmap() {
+ SkBitmap* bitmap = new SkBitmap();
+ bitmap->setConfig(SkBitmap::kARGB_8888_Config, 25, 25);
+ bitmap->allocPixels();
+ bitmap->eraseRGB(255, 0, 0);
+ return bitmap;
+}
+
+PlatformImage CreatePlatformImage() {
+ scoped_ptr<SkBitmap> bitmap(CreateBitmap());
+#if defined(OS_MACOSX)
+ NSImage* image = ::gfx::SkBitmapToNSImage(*(bitmap.get()));
+ base::mac::NSObjectRetain(image);
+ return image;
+#elif defined(OS_LINUX) && !defined(TOOLKIT_VIEWS)
+ return ::gfx::GdkPixbufFromSkBitmap(bitmap.get());
+#else
+ return bitmap.release();
+#endif
+}
+
+} // namespace test
+} // namespace gfx
+} // namespace ui
+
+#endif // UI_GFX_IMAGE_UNITTEST_H_