diff options
author | rsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-11 17:42:28 +0000 |
---|---|---|
committer | rsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-11 17:42:28 +0000 |
commit | 40a080e65b4205fc5f75dbbe43ca033a5a9664bd (patch) | |
tree | 188eb7f12d1dcd17238507d307539d18f52c6448 /ui | |
parent | d98dfa02191fbe47c1104b1129293b29f6600e87 (diff) | |
download | chromium_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.gyp | 5 | ||||
-rw-r--r-- | ui/gfx/image.cc | 281 | ||||
-rw-r--r-- | ui/gfx/image.h | 94 | ||||
-rw-r--r-- | ui/gfx/image_mac.mm | 20 | ||||
-rw-r--r-- | ui/gfx/image_unittest.cc | 109 | ||||
-rw-r--r-- | ui/gfx/image_unittest.h | 59 |
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_ |