diff options
Diffstat (limited to 'chromium/ui/gfx/image')
-rw-r--r-- | chromium/ui/gfx/image/OWNERS | 2 | ||||
-rw-r--r-- | chromium/ui/gfx/image/cairo_cached_surface.cc | 109 | ||||
-rw-r--r-- | chromium/ui/gfx/image/cairo_cached_surface.h | 84 | ||||
-rw-r--r-- | chromium/ui/gfx/image/image.cc | 393 | ||||
-rw-r--r-- | chromium/ui/gfx/image/image.h | 24 | ||||
-rw-r--r-- | chromium/ui/gfx/image/image_skia.cc | 94 | ||||
-rw-r--r-- | chromium/ui/gfx/image/image_skia.h | 4 | ||||
-rw-r--r-- | chromium/ui/gfx/image/image_skia_rep.cc | 19 | ||||
-rw-r--r-- | chromium/ui/gfx/image/image_skia_rep.h | 16 | ||||
-rw-r--r-- | chromium/ui/gfx/image/image_skia_unittest.cc | 185 | ||||
-rw-r--r-- | chromium/ui/gfx/image/image_skia_util_mac.h | 4 | ||||
-rw-r--r-- | chromium/ui/gfx/image/image_skia_util_mac.mm | 6 | ||||
-rw-r--r-- | chromium/ui/gfx/image/image_unittest.cc | 53 | ||||
-rw-r--r-- | chromium/ui/gfx/image/image_unittest_util.cc | 31 | ||||
-rw-r--r-- | chromium/ui/gfx/image/image_unittest_util.h | 2 | ||||
-rw-r--r-- | chromium/ui/gfx/image/image_util.cc | 57 | ||||
-rw-r--r-- | chromium/ui/gfx/image/image_util.h | 17 | ||||
-rw-r--r-- | chromium/ui/gfx/image/image_util_unittest.cc | 78 |
18 files changed, 588 insertions, 590 deletions
diff --git a/chromium/ui/gfx/image/OWNERS b/chromium/ui/gfx/image/OWNERS index d3273727c23..23dbb5a958f 100644 --- a/chromium/ui/gfx/image/OWNERS +++ b/chromium/ui/gfx/image/OWNERS @@ -1,4 +1,4 @@ rsesek@chromium.org # ImageSkia related classes except for _mac/_ios stuff -per-file image_skia.*=oshima@chromium.org +per-file image_skia*=oshima@chromium.org diff --git a/chromium/ui/gfx/image/cairo_cached_surface.cc b/chromium/ui/gfx/image/cairo_cached_surface.cc deleted file mode 100644 index 2de4a3210b3..00000000000 --- a/chromium/ui/gfx/image/cairo_cached_surface.cc +++ /dev/null @@ -1,109 +0,0 @@ -// 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/cairo_cached_surface.h" - -#include <gtk/gtk.h> - -#include "base/basictypes.h" -#include "base/logging.h" - -namespace gfx { - -CairoCachedSurface::CairoCachedSurface() : pixbuf_(NULL) { -} - -CairoCachedSurface::~CairoCachedSurface() { - Reset(); -} - -void CairoCachedSurface::Reset() { - for (SurfaceVector::iterator it = surface_map_.begin(); - it != surface_map_.end(); ++it) { - cairo_surface_destroy(it->second); - } - surface_map_.clear(); - - if (pixbuf_) { - g_object_unref(pixbuf_); - pixbuf_ = NULL; - } -} - -int CairoCachedSurface::Width() const { - return pixbuf_ ? gdk_pixbuf_get_width(pixbuf_) : -1; -} - -int CairoCachedSurface::Height() const { - return pixbuf_ ? gdk_pixbuf_get_height(pixbuf_) : -1; -} - -void CairoCachedSurface::UsePixbuf(GdkPixbuf* pixbuf) { - if (pixbuf) - g_object_ref(pixbuf); - - Reset(); - - pixbuf_ = pixbuf; -} - -void CairoCachedSurface::SetSource(cairo_t* cr, GtkWidget* widget, - int x, int y) const { - SetSource(cr, gtk_widget_get_display(widget), x, y); -} - -void CairoCachedSurface::SetSource(cairo_t* cr, GdkDisplay* display, - int x, int y) const { - DCHECK(pixbuf_); - DCHECK(cr); - DCHECK(display); - - cairo_surface_t* surface = GetSurfaceFor(cr, display); - cairo_set_source_surface(cr, surface, x, y); -} - -void CairoCachedSurface::MaskSource(cairo_t* cr, GtkWidget* widget, - int x, int y) const { - MaskSource(cr, gtk_widget_get_display(widget), x, y); -} - -void CairoCachedSurface::MaskSource(cairo_t* cr, GdkDisplay* display, - int x, int y) const { - DCHECK(pixbuf_); - DCHECK(cr); - DCHECK(display); - - cairo_surface_t* surface = GetSurfaceFor(cr, display); - cairo_mask_surface(cr, surface, x, y); -} - -cairo_surface_t* CairoCachedSurface::GetSurfaceFor(cairo_t* cr, - GdkDisplay* display) const { - for (SurfaceVector::const_iterator it = surface_map_.begin(); - it != surface_map_.end(); ++it) { - if (display == it->first) { - return it->second; - } - } - - // First time here since last UsePixbuf call. Generate the surface. - cairo_surface_t* target = cairo_get_target(cr); - cairo_surface_t* out = cairo_surface_create_similar( - target, - CAIRO_CONTENT_COLOR_ALPHA, - gdk_pixbuf_get_width(pixbuf_), - gdk_pixbuf_get_height(pixbuf_)); - - DCHECK(out); - - cairo_t* copy_cr = cairo_create(out); - gdk_cairo_set_source_pixbuf(copy_cr, pixbuf_, 0, 0); - cairo_paint(copy_cr); - cairo_destroy(copy_cr); - - surface_map_.push_back(std::make_pair(display, out)); - return out; -} - -} // namespace gfx diff --git a/chromium/ui/gfx/image/cairo_cached_surface.h b/chromium/ui/gfx/image/cairo_cached_surface.h deleted file mode 100644 index ceece4ba2d2..00000000000 --- a/chromium/ui/gfx/image/cairo_cached_surface.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2012 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_CAIRO_CACHED_SURFACE_H_ -#define UI_GFX_IMAGE_CAIRO_CACHED_SURFACE_H_ - -#include <vector> - -#include "ui/gfx/gfx_export.h" - -typedef struct _GdkDisplay GdkDisplay; -typedef struct _GdkPixbuf GdkPixbuf; -typedef struct _GtkWidget GtkWidget; -typedef struct _cairo cairo_t; -typedef struct _cairo_surface cairo_surface_t; - -namespace gfx { - -// A helper class that takes a GdkPixbuf* and renders it to the screen. Unlike -// gdk_cairo_set_source_pixbuf(), CairoCachedSurface assumes that the pixbuf is -// immutable after UsePixbuf() is called and can be sent to the display server -// once. From then on, that cached version is used so we don't upload the same -// image each and every time we expose. -// -// Most cached surfaces are owned by the GtkThemeService, which associates -// them with a certain XDisplay. Some users of surfaces (CustomDrawButtonBase, -// for example) own their surfaces instead since they interact with the -// ResourceBundle instead of the GtkThemeService. -class GFX_EXPORT CairoCachedSurface { - public: - CairoCachedSurface(); - ~CairoCachedSurface(); - - // Whether this CairoCachedSurface owns a GdkPixbuf. - bool valid() const { - return pixbuf_; - } - - // Delete all our data. - void Reset(); - - // The dimensions of the underlying pixbuf/surface. (or -1 if invalid.) - int Width() const; - int Height() const; - - // Sets the pixbuf that we pass to cairo. Calling UsePixbuf() only derefs the - // current pixbuf and surface (if they exist). Actually transfering data to - // the X server occurs at SetSource() time. Calling UsePixbuf() should only - // be done once as it clears cached data from the X server. - void UsePixbuf(GdkPixbuf* pixbuf); - - // Sets our pixbuf as the active surface starting at (x, y), uploading it in - // case we don't have an X backed surface cached. - void SetSource(cairo_t* cr, GtkWidget* widget, int x, int y) const; - void SetSource(cairo_t* cr, GdkDisplay* display, int x, int y) const; - - // Performs a mask operation, using this surface as the alpha channel. - void MaskSource(cairo_t* cr, GtkWidget* widget, int x, int y) const; - void MaskSource(cairo_t* cr, GdkDisplay* display, int x, int y) const; - - // Raw access to the pixbuf. May be NULL. Used for a few gdk operations - // regarding window shaping. - GdkPixbuf* pixbuf() { return pixbuf_; } - - private: - typedef std::vector<std::pair<GdkDisplay*, cairo_surface_t*> > SurfaceVector; - - // Returns a surface . Caches results so only one copy of the image data - // lives on the display server. - cairo_surface_t* GetSurfaceFor(cairo_t* cr, GdkDisplay* display) const; - - // The source pixbuf. - GdkPixbuf* pixbuf_; - - // Our list of cached surfaces. 99% of the time, this will only contain a - // single entry. At most two. We need to get this right for multiple displays - // to work correct, since each GdkDisplay is a different display server. - mutable SurfaceVector surface_map_; -}; - -} // namespace gfx - -#endif // UI_GFX_IMAGE_CAIRO_CACHED_SURFACE_H_ diff --git a/chromium/ui/gfx/image/image.cc b/chromium/ui/gfx/image/image.cc index 91b9e69b289..459880742f8 100644 --- a/chromium/ui/gfx/image/image.cc +++ b/chromium/ui/gfx/image/image.cc @@ -5,6 +5,7 @@ #include "ui/gfx/image/image.h" #include <algorithm> +#include <set> #include "base/logging.h" #include "base/memory/scoped_ptr.h" @@ -12,21 +13,14 @@ #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/image/image_png_rep.h" #include "ui/gfx/image/image_skia.h" +#include "ui/gfx/image/image_skia_source.h" #include "ui/gfx/size.h" #if !defined(OS_IOS) #include "ui/gfx/codec/png_codec.h" #endif -#if defined(TOOLKIT_GTK) -#include <gdk-pixbuf/gdk-pixbuf.h> -#include <gdk/gdk.h> -#include <glib-object.h> -#include "ui/gfx/canvas.h" -#include "ui/gfx/gtk_util.h" -#include "ui/gfx/image/cairo_cached_surface.h" -#include "ui/gfx/scoped_gobject.h" -#elif defined(OS_IOS) +#if defined(OS_IOS) #include "base/mac/foundation_util.h" #include "ui/gfx/image/image_skia_util_ios.h" #elif defined(OS_MACOSX) @@ -38,100 +32,25 @@ namespace gfx { namespace internal { -#if defined(TOOLKIT_GTK) -const ImageSkia ImageSkiaFromGdkPixbuf(GdkPixbuf* pixbuf) { - CHECK(pixbuf); - gfx::Canvas canvas(gfx::Size(gdk_pixbuf_get_width(pixbuf), - gdk_pixbuf_get_height(pixbuf)), - 1.0f, - false); - skia::ScopedPlatformPaint scoped_platform_paint(canvas.sk_canvas()); - cairo_t* cr = scoped_platform_paint.GetPlatformSurface(); - gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0); - cairo_paint(cr); - return ImageSkia(canvas.ExtractImageRep()); -} - -// Returns a 16x16 red pixbuf to visually show error in decoding PNG. -// Also logs error to console. -GdkPixbuf* GetErrorPixbuf() { - LOG(ERROR) << "Unable to decode PNG."; - GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 16, 16); - gdk_pixbuf_fill(pixbuf, 0xff0000ff); - return pixbuf; -} - -GdkPixbuf* GdkPixbufFromPNG( - const std::vector<gfx::ImagePNGRep>& image_png_reps) { - scoped_refptr<base::RefCountedMemory> png_bytes(NULL); - for (size_t i = 0; i < image_png_reps.size(); ++i) { - if (image_png_reps[i].scale == 1.0f) - png_bytes = image_png_reps[i].raw_data; - } - - if (!png_bytes.get()) - return GetErrorPixbuf(); - - GdkPixbuf* pixbuf = NULL; - ui::ScopedGObject<GdkPixbufLoader>::Type loader(gdk_pixbuf_loader_new()); - - bool ok = gdk_pixbuf_loader_write(loader.get(), - reinterpret_cast<const guint8*>(png_bytes->front()), png_bytes->size(), - NULL); - - // Calling gdk_pixbuf_loader_close forces the data to be parsed by the - // loader. This must be done before calling gdk_pixbuf_loader_get_pixbuf. - if (ok) - ok = gdk_pixbuf_loader_close(loader.get(), NULL); - if (ok) - pixbuf = gdk_pixbuf_loader_get_pixbuf(loader.get()); - - if (pixbuf) { - // The pixbuf is owned by the scoped loader which will delete its ref when - // it goes out of scope. Add a ref so that the pixbuf still exists. - g_object_ref(pixbuf); - } else { - return GetErrorPixbuf(); - } - - return pixbuf; -} - -scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromPixbuf( - GdkPixbuf* pixbuf) { - gchar* image = NULL; - gsize image_size; - GError* error = NULL; - CHECK(gdk_pixbuf_save_to_buffer( - pixbuf, &image, &image_size, "png", &error, NULL)); - scoped_refptr<base::RefCountedBytes> png_bytes( - new base::RefCountedBytes()); - png_bytes->data().assign(image, image + image_size); - g_free(image); - return png_bytes; -} - -#endif // defined(TOOLKIT_GTK) - #if defined(OS_IOS) scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromUIImage( UIImage* uiimage); // Caller takes ownership of the returned UIImage. UIImage* CreateUIImageFromPNG( - const std::vector<gfx::ImagePNGRep>& image_png_reps); + const std::vector<ImagePNGRep>& image_png_reps); gfx::Size UIImageSize(UIImage* image); #elif defined(OS_MACOSX) scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromNSImage( NSImage* nsimage); // Caller takes ownership of the returned NSImage. -NSImage* NSImageFromPNG(const std::vector<gfx::ImagePNGRep>& image_png_reps, +NSImage* NSImageFromPNG(const std::vector<ImagePNGRep>& image_png_reps, CGColorSpaceRef color_space); gfx::Size NSImageSize(NSImage* image); #endif // defined(OS_MACOSX) #if defined(OS_IOS) ImageSkia* ImageSkiaFromPNG( - const std::vector<gfx::ImagePNGRep>& image_png_reps); + const std::vector<ImagePNGRep>& image_png_reps); scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromImageSkia( const ImageSkia* skia); #else @@ -141,32 +60,92 @@ ImageSkia* GetErrorImageSkia() { SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, 16, 16); bitmap.allocPixels(); - bitmap.eraseRGB(0xff, 0, 0); - return new gfx::ImageSkia(gfx::ImageSkiaRep(bitmap, 1.0f)); + bitmap.eraseARGB(0xff, 0xff, 0, 0); + return new ImageSkia(ImageSkiaRep(bitmap, 1.0f)); } +class PNGImageSource : public ImageSkiaSource { + public: + PNGImageSource() {} + virtual ~PNGImageSource() {} + + virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE { + if (image_skia_reps_.empty()) + return ImageSkiaRep(); + + const ImageSkiaRep* rep = NULL; + // gfx::ImageSkia passes one of the resource scale factors. The source + // should return: + // 1) The ImageSkiaRep with the highest scale if all available + // scales are smaller than |scale|. + // 2) The ImageSkiaRep with the smallest one that is larger than |scale|. + for (ImageSkiaRepSet::const_iterator iter = image_skia_reps_.begin(); + iter != image_skia_reps_.end(); ++iter) { + if ((*iter).scale() == scale) + return (*iter); + if (!rep || rep->scale() < (*iter).scale()) + rep = &(*iter); + if (rep->scale() >= scale) + break; + } + return rep ? *rep : ImageSkiaRep(); + } + + const gfx::Size size() const { + return size_; + } + + bool AddPNGData(const ImagePNGRep& png_rep) { + const gfx::ImageSkiaRep rep = ToImageSkiaRep(png_rep); + if (rep.is_null()) + return false; + if (size_.IsEmpty()) + size_ = gfx::Size(rep.GetWidth(), rep.GetHeight()); + image_skia_reps_.insert(rep); + return true; + } + + static ImageSkiaRep ToImageSkiaRep(const ImagePNGRep& png_rep) { + scoped_refptr<base::RefCountedMemory> raw_data = png_rep.raw_data; + CHECK(raw_data.get()); + SkBitmap bitmap; + if (!PNGCodec::Decode(raw_data->front(), raw_data->size(), + &bitmap)) { + LOG(ERROR) << "Unable to decode PNG for " << png_rep.scale << "."; + return ImageSkiaRep(); + } + return ImageSkiaRep(bitmap, png_rep.scale); + } + + private: + struct Compare { + bool operator()(const ImageSkiaRep& rep1, const ImageSkiaRep& rep2) { + return rep1.scale() < rep2.scale(); + } + }; + + typedef std::set<ImageSkiaRep, Compare> ImageSkiaRepSet; + ImageSkiaRepSet image_skia_reps_; + gfx::Size size_; + + DISALLOW_COPY_AND_ASSIGN(PNGImageSource); +}; + ImageSkia* ImageSkiaFromPNG( - const std::vector<gfx::ImagePNGRep>& image_png_reps) { + const std::vector<ImagePNGRep>& image_png_reps) { if (image_png_reps.empty()) return GetErrorImageSkia(); + scoped_ptr<PNGImageSource> image_source(new PNGImageSource); - scoped_ptr<gfx::ImageSkia> image_skia(new ImageSkia()); for (size_t i = 0; i < image_png_reps.size(); ++i) { - scoped_refptr<base::RefCountedMemory> raw_data = - image_png_reps[i].raw_data; - CHECK(raw_data.get()); - SkBitmap bitmap; - if (!gfx::PNGCodec::Decode(raw_data->front(), raw_data->size(), - &bitmap)) { - LOG(ERROR) << "Unable to decode PNG for " - << image_png_reps[i].scale - << "."; + if (!image_source->AddPNGData(image_png_reps[i])) return GetErrorImageSkia(); - } - image_skia->AddRepresentation(gfx::ImageSkiaRep( - bitmap, image_png_reps[i].scale)); } - return image_skia.release(); + const gfx::Size& size = image_source->size(); + DCHECK(!size.IsEmpty()); + if (size.IsEmpty()) + return GetErrorImageSkia(); + return new ImageSkia(image_source.release(), size); } scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromImageSkia( @@ -175,7 +154,7 @@ scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromImageSkia( scoped_refptr<base::RefCountedBytes> png_bytes(new base::RefCountedBytes()); if (image_skia_rep.scale() != 1.0f || - !gfx::PNGCodec::EncodeBGRASkBitmap(image_skia_rep.sk_bitmap(), false, + !PNGCodec::EncodeBGRASkBitmap(image_skia_rep.sk_bitmap(), false, &png_bytes->data())) { return NULL; } @@ -185,8 +164,6 @@ scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromImageSkia( class ImageRepPNG; class ImageRepSkia; -class ImageRepGdk; -class ImageRepCairo; class ImageRepCocoa; class ImageRepCocoaTouch; @@ -213,18 +190,6 @@ class ImageRep { return reinterpret_cast<ImageRepSkia*>(this); } -#if defined(TOOLKIT_GTK) - ImageRepGdk* AsImageRepGdk() { - CHECK_EQ(type_, Image::kImageRepGdk); - return reinterpret_cast<ImageRepGdk*>(this); - } - - ImageRepCairo* AsImageRepCairo() { - CHECK_EQ(type_, Image::kImageRepCairo); - return reinterpret_cast<ImageRepCairo*>(this); - } -#endif - #if defined(OS_IOS) ImageRepCocoaTouch* AsImageRepCocoaTouch() { CHECK_EQ(type_, Image::kImageRepCocoaTouch); @@ -326,77 +291,6 @@ class ImageRepSkia : public ImageRep { DISALLOW_COPY_AND_ASSIGN(ImageRepSkia); }; -#if defined(TOOLKIT_GTK) -class ImageRepGdk : public ImageRep { - public: - explicit ImageRepGdk(GdkPixbuf* pixbuf) - : ImageRep(Image::kImageRepGdk), - pixbuf_(pixbuf) { - CHECK(pixbuf); - } - - virtual ~ImageRepGdk() { - if (pixbuf_) { - g_object_unref(pixbuf_); - pixbuf_ = NULL; - } - } - - virtual int Width() const OVERRIDE { - return gdk_pixbuf_get_width(pixbuf_); - } - - virtual int Height() const OVERRIDE { - return gdk_pixbuf_get_height(pixbuf_); - } - - virtual gfx::Size Size() const OVERRIDE { - return gfx::Size(Width(), Height()); - } - - GdkPixbuf* pixbuf() const { return pixbuf_; } - - private: - GdkPixbuf* pixbuf_; - - DISALLOW_COPY_AND_ASSIGN(ImageRepGdk); -}; - -// Represents data that lives on the display server instead of in the client. -class ImageRepCairo : public ImageRep { - public: - explicit ImageRepCairo(GdkPixbuf* pixbuf) - : ImageRep(Image::kImageRepCairo), - cairo_cache_(new CairoCachedSurface) { - CHECK(pixbuf); - cairo_cache_->UsePixbuf(pixbuf); - } - - virtual ~ImageRepCairo() { - delete cairo_cache_; - } - - virtual int Width() const OVERRIDE { - return cairo_cache_->Width(); - } - - virtual int Height() const OVERRIDE { - return cairo_cache_->Height(); - } - - virtual gfx::Size Size() const OVERRIDE { - return gfx::Size(Width(), Height()); - } - - CairoCachedSurface* surface() const { return cairo_cache_; } - - private: - CairoCachedSurface* cairo_cache_; - - DISALLOW_COPY_AND_ASSIGN(ImageRepCairo); -}; -#endif // defined(TOOLKIT_GTK) - #if defined(OS_IOS) class ImageRepCocoaTouch : public ImageRep { public: @@ -470,7 +364,7 @@ class ImageRepCocoa : public ImageRep { // ImageReps. This way, the Image can be cheaply copied. class ImageStorage : public base::RefCounted<ImageStorage> { public: - ImageStorage(gfx::Image::RepresentationType default_type) + ImageStorage(Image::RepresentationType default_type) : default_representation_type_(default_type), #if defined(OS_MACOSX) && !defined(OS_IOS) default_representation_color_space_( @@ -479,10 +373,10 @@ class ImageStorage : public base::RefCounted<ImageStorage> { representations_deleter_(&representations_) { } - gfx::Image::RepresentationType default_representation_type() { + Image::RepresentationType default_representation_type() { return default_representation_type_; } - gfx::Image::RepresentationMap& representations() { return representations_; } + Image::RepresentationMap& representations() { return representations_; } #if defined(OS_MACOSX) && !defined(OS_IOS) void set_default_representation_color_space(CGColorSpaceRef color_space) { @@ -500,7 +394,7 @@ class ImageStorage : public base::RefCounted<ImageStorage> { // The type of image that was passed to the constructor. This key will always // exist in the |representations_| map. - gfx::Image::RepresentationType default_representation_type_; + Image::RepresentationType default_representation_type_; #if defined(OS_MACOSX) && !defined(OS_IOS) // The default representation's colorspace. This is used for converting to @@ -512,7 +406,7 @@ class ImageStorage : public base::RefCounted<ImageStorage> { // All the representations of an Image. Size will always be at least one, with // more for any converted representations. - gfx::Image::RepresentationMap representations_; + Image::RepresentationMap representations_; STLValueDeleter<Image::RepresentationMap> representations_deleter_; @@ -550,16 +444,6 @@ Image::Image(const ImageSkia& image) { } } -#if defined(TOOLKIT_GTK) -Image::Image(GdkPixbuf* pixbuf) { - if (pixbuf) { - storage_ = new internal::ImageStorage(Image::kImageRepGdk); - internal::ImageRepGdk* rep = new internal::ImageRepGdk(pixbuf); - AddRepresentation(rep); - } -} -#endif - #if defined(OS_IOS) Image::Image(UIImage* image) : storage_(new internal::ImageStorage(Image::kImageRepCocoaTouch)) { @@ -591,20 +475,29 @@ Image::~Image() { // static Image Image::CreateFrom1xBitmap(const SkBitmap& bitmap) { - return gfx::Image(ImageSkia::CreateFrom1xBitmap(bitmap)); + return Image(ImageSkia::CreateFrom1xBitmap(bitmap)); } // static Image Image::CreateFrom1xPNGBytes(const unsigned char* input, size_t input_size) { if (input_size == 0u) - return gfx::Image(); + return Image(); scoped_refptr<base::RefCountedBytes> raw_data(new base::RefCountedBytes()); raw_data->data().assign(input, input + input_size); - std::vector<gfx::ImagePNGRep> image_reps; - image_reps.push_back(ImagePNGRep(raw_data, 1.0f)); - return gfx::Image(image_reps); + + return CreateFrom1xPNGBytes(raw_data); +} + +Image Image::CreateFrom1xPNGBytes( + const scoped_refptr<base::RefCountedMemory>& input) { + if (!input.get() || input->size() == 0u) + return Image(); + + std::vector<ImagePNGRep> image_reps; + image_reps.push_back(ImagePNGRep(input, 1.0f)); + return Image(image_reps); } const SkBitmap* Image::ToSkBitmap() const { @@ -623,15 +516,7 @@ const ImageSkia* Image::ToImageSkia() const { internal::ImageSkiaFromPNG(png_rep->image_reps())); break; } -#if defined(TOOLKIT_GTK) - case kImageRepGdk: { - internal::ImageRepGdk* native_rep = - GetRepresentation(kImageRepGdk, true)->AsImageRepGdk(); - rep = new internal::ImageRepSkia(new ImageSkia( - internal::ImageSkiaFromGdkPixbuf(native_rep->pixbuf()))); - break; - } -#elif defined(OS_IOS) +#if defined(OS_IOS) case kImageRepCocoaTouch: { internal::ImageRepCocoaTouch* native_rep = GetRepresentation(kImageRepCocoaTouch, true) @@ -658,47 +543,6 @@ const ImageSkia* Image::ToImageSkia() const { return rep->AsImageRepSkia()->image(); } -#if defined(TOOLKIT_GTK) -GdkPixbuf* Image::ToGdkPixbuf() const { - internal::ImageRep* rep = GetRepresentation(kImageRepGdk, false); - if (!rep) { - switch (DefaultRepresentationType()) { - case kImageRepPNG: { - internal::ImageRepPNG* png_rep = - GetRepresentation(kImageRepPNG, true)->AsImageRepPNG(); - rep = new internal::ImageRepGdk(internal::GdkPixbufFromPNG( - png_rep->image_reps())); - break; - } - case kImageRepSkia: { - internal::ImageRepSkia* skia_rep = - GetRepresentation(kImageRepSkia, true)->AsImageRepSkia(); - rep = new internal::ImageRepGdk(gfx::GdkPixbufFromSkBitmap( - *skia_rep->image()->bitmap())); - break; - } - default: - NOTREACHED(); - } - CHECK(rep); - AddRepresentation(rep); - } - return rep->AsImageRepGdk()->pixbuf(); -} - -CairoCachedSurface* const Image::ToCairo() const { - internal::ImageRep* rep = GetRepresentation(kImageRepCairo, false); - if (!rep) { - // Handle any-to-Cairo conversion. This may create and cache an intermediate - // pixbuf before sending the data to the display server. - rep = new internal::ImageRepCairo(ToGdkPixbuf()); - CHECK(rep); - AddRepresentation(rep); - } - return rep->AsImageRepCairo()->surface(); -} -#endif - #if defined(OS_IOS) UIImage* Image::ToUIImage() const { internal::ImageRep* rep = GetRepresentation(kImageRepCocoaTouch, false); @@ -768,7 +612,7 @@ scoped_refptr<base::RefCountedMemory> Image::As1xPNGBytes() const { internal::ImageRep* rep = GetRepresentation(kImageRepPNG, false); if (rep) { - const std::vector<gfx::ImagePNGRep>& image_png_reps = + const std::vector<ImagePNGRep>& image_png_reps = rep->AsImageRepPNG()->image_reps(); for (size_t i = 0; i < image_png_reps.size(); ++i) { if (image_png_reps[i].scale == 1.0f) @@ -779,14 +623,7 @@ scoped_refptr<base::RefCountedMemory> Image::As1xPNGBytes() const { scoped_refptr<base::RefCountedMemory> png_bytes(NULL); switch (DefaultRepresentationType()) { -#if defined(TOOLKIT_GTK) - case kImageRepGdk: { - internal::ImageRepGdk* gdk_rep = - GetRepresentation(kImageRepGdk, true)->AsImageRepGdk(); - png_bytes = internal::Get1xPNGBytesFromPixbuf(gdk_rep->pixbuf()); - break; - } -#elif defined(OS_IOS) +#if defined(OS_IOS) case kImageRepCocoaTouch: { internal::ImageRepCocoaTouch* cocoa_touch_rep = GetRepresentation(kImageRepCocoaTouch, true) @@ -826,7 +663,7 @@ scoped_refptr<base::RefCountedMemory> Image::As1xPNGBytes() const { // final type eg (converting from ImageRepSkia to ImageRepPNG to get an // ImageRepCocoa). std::vector<ImagePNGRep> image_png_reps; - image_png_reps.push_back(gfx::ImagePNGRep(png_bytes, 1.0f)); + image_png_reps.push_back(ImagePNGRep(png_bytes, 1.0f)); rep = new internal::ImageRepPNG(image_png_reps); AddRepresentation(rep); return png_bytes; @@ -861,14 +698,6 @@ SkBitmap* Image::CopySkBitmap() const { return new SkBitmap(*ToSkBitmap()); } -#if defined(TOOLKIT_GTK) -GdkPixbuf* Image::CopyGdkPixbuf() const { - GdkPixbuf* pixbuf = ToGdkPixbuf(); - g_object_ref(pixbuf); - return pixbuf; -} -#endif - #if defined(OS_IOS) UIImage* Image::CopyUIImage() const { UIImage* image = ToUIImage(); @@ -916,7 +745,7 @@ gfx::Size Image::Size() const { return GetRepresentation(DefaultRepresentationType(), true)->Size(); } -void Image::SwapRepresentations(gfx::Image* other) { +void Image::SwapRepresentations(Image* other) { storage_.swap(other->storage_); } @@ -929,11 +758,7 @@ void Image::SetSourceColorSpace(CGColorSpaceRef color_space) { Image::RepresentationType Image::DefaultRepresentationType() const { CHECK(storage_.get()); - RepresentationType default_type = storage_->default_representation_type(); - // The conversions above assume that the default representation type is never - // kImageRepCairo. - DCHECK_NE(default_type, kImageRepCairo); - return default_type; + return storage_->default_representation_type(); } internal::ImageRep* Image::GetRepresentation( diff --git a/chromium/ui/gfx/image/image.h b/chromium/ui/gfx/image/image.h index d4093b74063..19ca93dada9 100644 --- a/chromium/ui/gfx/image/image.h +++ b/chromium/ui/gfx/image/image.h @@ -44,10 +44,6 @@ struct ImagePNGRep; class ImageSkia; class Size; -#if defined(TOOLKIT_GTK) -class CairoCachedSurface; -#endif - namespace internal { class ImageRep; class ImageStorage; @@ -56,10 +52,8 @@ class ImageStorage; class GFX_EXPORT Image { public: enum RepresentationType { - kImageRepGdk, kImageRepCocoa, kImageRepCocoaTouch, - kImageRepCairo, kImageRepSkia, kImageRepPNG, }; @@ -77,10 +71,7 @@ class GFX_EXPORT Image { // representation. explicit Image(const ImageSkia& image); -#if defined(TOOLKIT_GTK) - // Does not increase |pixbuf|'s reference count; expects to take ownership. - explicit Image(GdkPixbuf* pixbuf); -#elif defined(OS_IOS) +#if defined(OS_IOS) // Does not retain |image|; expects to take ownership. explicit Image(UIImage* image); #elif defined(OS_MACOSX) @@ -113,15 +104,16 @@ class GFX_EXPORT Image { static Image CreateFrom1xPNGBytes(const unsigned char* input, size_t input_size); + // Creates an image from the PNG encoded input. + static Image CreateFrom1xPNGBytes( + const scoped_refptr<base::RefCountedMemory>& input); + // Converts the Image to the desired representation and stores it internally. // The returned result is a weak pointer owned by and scoped to the life of // the Image. Must only be called if IsEmpty() is false. const SkBitmap* ToSkBitmap() const; const ImageSkia* ToImageSkia() const; -#if defined(TOOLKIT_GTK) - GdkPixbuf* ToGdkPixbuf() const; - CairoCachedSurface* const ToCairo() const; -#elif defined(OS_IOS) +#if defined(OS_IOS) UIImage* ToUIImage() const; #elif defined(OS_MACOSX) NSImage* ToNSImage() const; @@ -154,9 +146,7 @@ class GFX_EXPORT Image { scoped_refptr<base::RefCountedMemory> Copy1xPNGBytes() const; ImageSkia* CopyImageSkia() const; SkBitmap* CopySkBitmap() const; -#if defined(TOOLKIT_GTK) - GdkPixbuf* CopyGdkPixbuf() const; -#elif defined(OS_IOS) +#if defined(OS_IOS) UIImage* CopyUIImage() const; #elif defined(OS_MACOSX) NSImage* CopyNSImage() const; diff --git a/chromium/ui/gfx/image/image_skia.cc b/chromium/ui/gfx/image/image_skia.cc index a283117db24..21d08de3003 100644 --- a/chromium/ui/gfx/image/image_skia.cc +++ b/chromium/ui/gfx/image/image_skia.cc @@ -8,14 +8,17 @@ #include <cmath> #include <limits> +#include "base/command_line.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/threading/non_thread_safe.h" +#include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/image/image_skia_source.h" #include "ui/gfx/rect.h" #include "ui/gfx/size.h" #include "ui/gfx/skia_util.h" +#include "ui/gfx/switches.h" namespace gfx { namespace { @@ -27,6 +30,13 @@ gfx::ImageSkiaRep& NullImageRep() { } std::vector<float>* g_supported_scales = NULL; + +// The difference to fall back to the smaller scale factor rather than the +// larger one. For example, assume 1.25 is requested but only 1.0 and 2.0 are +// supported. In that case, not fall back to 2.0 but 1.0, and then expand +// the image to 1.25. +const float kFallbackToSmallerScaleDiff = 0.25f; + } // namespace namespace internal { @@ -102,12 +112,36 @@ class ImageSkiaStorage : public base::RefCountedThreadSafe<ImageSkiaStorage>, return (read_only_ && !source_.get()) || CalledOnValidThread(); } + // Add a new representation. This checks if the scale of the added image + // is not 1.0f, and mark the existing rep as scaled to make + // the image high DPI aware. + void AddRepresentation(const ImageSkiaRep& image) { + if (image.scale() != 1.0f) { + for (ImageSkia::ImageSkiaReps::iterator it = image_reps_.begin(); + it < image_reps_.end(); + ++it) { + if (it->unscaled()) { + DCHECK_EQ(1.0f, it->scale()); + it->SetScaled(); + break; + } + } + } + image_reps_.push_back(image); + } + // Returns the iterator of the image rep whose density best matches // |scale|. If the image for the |scale| doesn't exist in the storage and // |storage| is set, it fetches new image by calling - // |ImageSkiaSource::GetImageForScale|. If the source returns the image with - // different scale (if the image doesn't exist in resource, for example), it - // will fallback to closest image rep. + // |ImageSkiaSource::GetImageForScale|. There are two modes to deal with + // arbitrary scale factors. + // 1: Invoke GetImageForScale with requested scale and if the source + // returns the image with different scale (if the image doesn't exist in + // resource, for example), it will fallback to closest image rep. + // 2: Invoke GetImageForScale with the closest known scale to the requested + // one and rescale the image. + // Right now only Windows uses 2 and other platforms use 1 by default. + // TODO(mukai, oshima): abandon 1 code path and use 2 for every platforms. std::vector<ImageSkiaRep>::iterator FindRepresentation( float scale, bool fetch_new_image) const { ImageSkiaStorage* non_const = const_cast<ImageSkiaStorage*>(this); @@ -139,7 +173,47 @@ class ImageSkiaStorage : public base::RefCountedThreadSafe<ImageSkiaStorage>, DCHECK(CalledOnValidThread()) << "An ImageSkia with the source must be accessed by the same thread."; - ImageSkiaRep image = source_->GetImageForScale(scale); + ImageSkiaRep image; + float resource_scale = scale; + if (ImageSkia::IsDSFScalingInImageSkiaEnabled() && g_supported_scales) { + if (g_supported_scales->back() <= scale) { + resource_scale = g_supported_scales->back(); + } else { + for (size_t i = 0; i < g_supported_scales->size(); ++i) { + if ((*g_supported_scales)[i] + kFallbackToSmallerScaleDiff >= + scale) { + resource_scale = (*g_supported_scales)[i]; + break; + } + } + } + } + if (ImageSkia::IsDSFScalingInImageSkiaEnabled() && + scale != resource_scale) { + std::vector<ImageSkiaRep>::iterator iter = FindRepresentation( + resource_scale, fetch_new_image); + + DCHECK(iter != image_reps_.end()); + + if (!iter->unscaled()) { + SkBitmap scaled_image; + gfx::Size unscaled_size(iter->pixel_width(), iter->pixel_height()); + gfx::Size scaled_size = ToCeiledSize( + gfx::ScaleSize(unscaled_size, scale / iter->scale())); + + image = ImageSkiaRep(skia::ImageOperations::Resize( + iter->sk_bitmap(), + skia::ImageOperations::RESIZE_LANCZOS3, + scaled_size.width(), + scaled_size.height()), scale); + DCHECK_EQ(image.pixel_width(), scaled_size.width()); + DCHECK_EQ(image.pixel_height(), scaled_size.height()); + } else { + image = *iter; + } + } else { + image = source_->GetImageForScale(scale); + } // If the source returned the new image, store it. if (!image.is_null() && @@ -240,7 +314,13 @@ float ImageSkia::GetMaxSupportedScale() { // static ImageSkia ImageSkia::CreateFrom1xBitmap(const SkBitmap& bitmap) { - return ImageSkia(ImageSkiaRep(bitmap, 1.0f)); + return ImageSkia(ImageSkiaRep(bitmap, 0.0f)); +} + +bool ImageSkia::IsDSFScalingInImageSkiaEnabled() { + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + return !command_line->HasSwitch( + switches::kDisableArbitraryScaleFactorInImageSkia); } scoped_ptr<ImageSkia> ImageSkia::DeepCopy() const { @@ -278,7 +358,9 @@ void ImageSkia::AddRepresentation(const ImageSkiaRep& image_rep) { Init(image_rep); } else { CHECK(CanModify()); - storage_->image_reps().push_back(image_rep); + // If someone is adding ImageSkia explicitly, check if we should + // make the image high DPI aware. + storage_->AddRepresentation(image_rep); } } diff --git a/chromium/ui/gfx/image/image_skia.h b/chromium/ui/gfx/image/image_skia.h index fa5d070ad9b..8765457b432 100644 --- a/chromium/ui/gfx/image/image_skia.h +++ b/chromium/ui/gfx/image/image_skia.h @@ -78,6 +78,10 @@ class GFX_EXPORT ImageSkia { // density display. static ImageSkia CreateFrom1xBitmap(const SkBitmap& bitmap); + // Returns true when ImageSkia looks up the resource pack with the closest + // scale factor and rescale the fetched image. + static bool IsDSFScalingInImageSkiaEnabled(); + // Returns a deep copy of this ImageSkia which has its own storage with // the ImageSkiaRep instances that this ImageSkia currently has. // This can be safely passed to and manipulated by another thread. diff --git a/chromium/ui/gfx/image/image_skia_rep.cc b/chromium/ui/gfx/image/image_skia_rep.cc index f8f3acb1946..f31db600465 100644 --- a/chromium/ui/gfx/image/image_skia_rep.cc +++ b/chromium/ui/gfx/image/image_skia_rep.cc @@ -4,9 +4,11 @@ #include "ui/gfx/image/image_skia_rep.h" +#include "base/logging.h" + namespace gfx { -ImageSkiaRep::ImageSkiaRep() : scale_(1.0f) { +ImageSkiaRep::ImageSkiaRep() : scale_(0.0f) { } ImageSkiaRep::~ImageSkiaRep() { @@ -14,9 +16,10 @@ ImageSkiaRep::~ImageSkiaRep() { ImageSkiaRep::ImageSkiaRep(const gfx::Size& size, float scale) : scale_(scale) { bitmap_.setConfig(SkBitmap::kARGB_8888_Config, - static_cast<int>(size.width() * scale), - static_cast<int>(size.height() * scale)); + static_cast<int>(size.width() * this->scale()), + static_cast<int>(size.height() * this->scale())); bitmap_.allocPixels(); + bitmap_.eraseColor(SK_ColorRED); } ImageSkiaRep::ImageSkiaRep(const SkBitmap& src, float scale) @@ -25,11 +28,17 @@ ImageSkiaRep::ImageSkiaRep(const SkBitmap& src, float scale) } int ImageSkiaRep::GetWidth() const { - return static_cast<int>(bitmap_.width() / scale_); + return static_cast<int>(bitmap_.width() / scale()); } int ImageSkiaRep::GetHeight() const { - return static_cast<int>(bitmap_.height() / scale_); + return static_cast<int>(bitmap_.height() / scale()); +} + +void ImageSkiaRep::SetScaled() { + DCHECK_EQ(0.0f, scale_); + if (scale_ == 0.0f) + scale_ = 1.0f; } } // namespace gfx diff --git a/chromium/ui/gfx/image/image_skia_rep.h b/chromium/ui/gfx/image/image_skia_rep.h index 7a1e83709ab..47f6713acf5 100644 --- a/chromium/ui/gfx/image/image_skia_rep.h +++ b/chromium/ui/gfx/image/image_skia_rep.h @@ -12,14 +12,20 @@ namespace gfx { // An ImageSkiaRep represents a bitmap and the scale factor it is intended for. +// 0.0f scale is used to indicate that this ImageSkiaRep is used for unscaled +// image (the image that only returns 1.0f scale image). class GFX_EXPORT ImageSkiaRep { public: // Create null bitmap. ImageSkiaRep(); ~ImageSkiaRep(); + // Note: This is for testing purpose only. // Creates a bitmap with kARGB_8888_Config config with given |size| in DIP. - // This allocates pixels in the bitmap. + // This allocates pixels in the bitmap. It is guaranteed that the data in the + // bitmap are initialized but the actual values are undefined. + // Specifying 0 scale means the image is for unscaled image. (unscaled() + // returns truen, and scale() returns 1.0f;) ImageSkiaRep(const gfx::Size& size, float scale); // Creates a bitmap with given scale. @@ -41,7 +47,12 @@ class GFX_EXPORT ImageSkiaRep { } // Retrieves the scale that the bitmap will be painted at. - float scale() const { return scale_; } + float scale() const { return unscaled() ? 1.0f : scale_; } + + bool unscaled() const { return scale_ == 0.0f; } + + // Mark the image to be used as scaled image. + void SetScaled(); // Returns backing bitmap. const SkBitmap& sk_bitmap() const { return bitmap_; } @@ -51,6 +62,7 @@ class GFX_EXPORT ImageSkiaRep { SkBitmap& mutable_sk_bitmap() { return bitmap_; } SkBitmap bitmap_; + float scale_; }; diff --git a/chromium/ui/gfx/image/image_skia_unittest.cc b/chromium/ui/gfx/image/image_skia_unittest.cc index 0047822122b..e3e93c73b78 100644 --- a/chromium/ui/gfx/image/image_skia_unittest.cc +++ b/chromium/ui/gfx/image/image_skia_unittest.cc @@ -4,6 +4,7 @@ #include "ui/gfx/image/image_skia.h" +#include "base/command_line.h" #include "base/logging.h" #include "base/threading/simple_thread.h" #include "testing/gtest/include/gtest/gtest.h" @@ -11,6 +12,7 @@ #include "ui/gfx/image/image_skia_rep.h" #include "ui/gfx/image/image_skia_source.h" #include "ui/gfx/size.h" +#include "ui/gfx/switches.h" // Duplicated from base/threading/non_thread_safe.h so that we can be // good citizens there and undef the macro. @@ -43,17 +45,27 @@ class FixedSource : public ImageSkiaSource { class DynamicSource : public ImageSkiaSource { public: - DynamicSource(const gfx::Size& size) : size_(size) {} + DynamicSource(const gfx::Size& size) + : size_(size), + last_requested_scale_(0.0f) {} virtual ~DynamicSource() { } virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE { + last_requested_scale_ = scale; return gfx::ImageSkiaRep(size_, scale); } + float GetLastRequestedScaleAndReset() { + float result = last_requested_scale_; + last_requested_scale_ = 0.0f; + return result; + } + private: gfx::Size size_; + float last_requested_scale_; DISALLOW_COPY_AND_ASSIGN(DynamicSource); }; @@ -113,7 +125,28 @@ class TestOnThread : public base::SimpleThread { } // namespace test -TEST(ImageSkiaTest, FixedSource) { +class ImageSkiaTest : public testing::Test { + public: + ImageSkiaTest() { + // In the test, we assume that we support 1.0f and 2.0f DSFs. + old_scales_ = ImageSkia::GetSupportedScales(); + + // Sets the list of scale factors supported by resource bundle. + std::vector<float> supported_scales; + supported_scales.push_back(1.0f); + supported_scales.push_back(2.0f); + ImageSkia::SetSupportedScales(supported_scales); + } + virtual ~ImageSkiaTest() { + ImageSkia::SetSupportedScales(old_scales_); + } + + private: + std::vector<float> old_scales_; + DISALLOW_COPY_AND_ASSIGN(ImageSkiaTest); +}; + +TEST_F(ImageSkiaTest, FixedSource) { ImageSkiaRep image(Size(100, 200), 1.0f); ImageSkia image_skia(new FixedSource(image), Size(100, 200)); EXPECT_EQ(0U, image_skia.image_reps().size()); @@ -140,7 +173,7 @@ TEST(ImageSkiaTest, FixedSource) { EXPECT_EQ(1U, image_skia.image_reps().size()); } -TEST(ImageSkiaTest, DynamicSource) { +TEST_F(ImageSkiaTest, DynamicSource) { ImageSkia image_skia(new DynamicSource(Size(100, 200)), Size(100, 200)); EXPECT_EQ(0U, image_skia.image_reps().size()); const ImageSkiaRep& result_100p = image_skia.GetRepresentation(1.0f); @@ -169,7 +202,7 @@ TEST(ImageSkiaTest, DynamicSource) { // Tests that image_reps returns all of the representations in the // image when there are multiple representations for a scale factor. // This currently is the case with ImageLoader::LoadImages. -TEST(ImageSkiaTest, ManyRepsPerScaleFactor) { +TEST_F(ImageSkiaTest, ManyRepsPerScaleFactor) { const int kSmallIcon1x = 16; const int kSmallIcon2x = 32; const int kLargeIcon1x = 32; @@ -204,14 +237,14 @@ TEST(ImageSkiaTest, ManyRepsPerScaleFactor) { EXPECT_EQ(1, num_2x); } -TEST(ImageSkiaTest, GetBitmap) { +TEST_F(ImageSkiaTest, GetBitmap) { ImageSkia image_skia(new DynamicSource(Size(100, 200)), Size(100, 200)); const SkBitmap* bitmap = image_skia.bitmap(); EXPECT_NE(static_cast<SkBitmap*>(NULL), bitmap); EXPECT_FALSE(bitmap->isNull()); } -TEST(ImageSkiaTest, GetBitmapFromEmpty) { +TEST_F(ImageSkiaTest, GetBitmapFromEmpty) { // Create an image with 1 representation and remove it so the ImageSkiaStorage // is left with no representations. ImageSkia empty_image(ImageSkiaRep(Size(100, 200), 1.0f)); @@ -226,7 +259,7 @@ TEST(ImageSkiaTest, GetBitmapFromEmpty) { EXPECT_TRUE(bitmap->empty()); } -TEST(ImageSkiaTest, BackedBySameObjectAs) { +TEST_F(ImageSkiaTest, BackedBySameObjectAs) { // Null images should all be backed by the same object (NULL). ImageSkia image; ImageSkia unrelated; @@ -245,7 +278,7 @@ TEST(ImageSkiaTest, BackedBySameObjectAs) { } #if ENABLE_NON_THREAD_SAFE -TEST(ImageSkiaTest, EmptyOnThreadTest) { +TEST_F(ImageSkiaTest, EmptyOnThreadTest) { ImageSkia empty; test::TestOnThread empty_on_thread(&empty); empty_on_thread.Start(); @@ -254,7 +287,7 @@ TEST(ImageSkiaTest, EmptyOnThreadTest) { EXPECT_TRUE(empty_on_thread.can_modify()); } -TEST(ImageSkiaTest, StaticOnThreadTest) { +TEST_F(ImageSkiaTest, StaticOnThreadTest) { ImageSkia image(ImageSkiaRep(Size(100, 200), 1.0f)); EXPECT_FALSE(image.IsThreadSafe()); @@ -323,7 +356,7 @@ TEST(ImageSkiaTest, StaticOnThreadTest) { EXPECT_FALSE(image.CanModify()); } -TEST(ImageSkiaTest, SourceOnThreadTest) { +TEST_F(ImageSkiaTest, SourceOnThreadTest) { ImageSkia image(new DynamicSource(Size(100, 200)), Size(100, 200)); EXPECT_FALSE(image.IsThreadSafe()); @@ -375,4 +408,136 @@ TEST(ImageSkiaTest, SourceOnThreadTest) { // Just in case we ever get lumped together with other compilation units. #undef ENABLE_NON_THREAD_SAFE +TEST_F(ImageSkiaTest, Unscaled) { + SkBitmap bitmap; + + // An ImageSkia created with 1x bitmap is unscaled. + ImageSkia image_skia = ImageSkia::CreateFrom1xBitmap(bitmap); + EXPECT_TRUE(image_skia.GetRepresentation(1.0f).unscaled()); + ImageSkiaRep rep_2x(Size(100, 100), 2.0f); + + // When reps for other scales are added, the unscaled image + // becomes scaled. + image_skia.AddRepresentation(rep_2x); + EXPECT_FALSE(image_skia.GetRepresentation(1.0f).unscaled()); + EXPECT_FALSE(image_skia.GetRepresentation(2.0f).unscaled()); +} + +TEST_F(ImageSkiaTest, ArbitraryScaleFactor) { + // Do not test if the ImageSkia doesn't support arbitrary scale factors. + if (!ImageSkia::IsDSFScalingInImageSkiaEnabled()) + return; + + // source is owned by |image| + DynamicSource* source = new DynamicSource(Size(100, 200)); + ImageSkia image(source, gfx::Size(100, 200)); + + image.GetRepresentation(1.5f); + EXPECT_EQ(2.0f, source->GetLastRequestedScaleAndReset()); + std::vector<ImageSkiaRep> image_reps = image.image_reps(); + EXPECT_EQ(2u, image_reps.size()); + + std::vector<float> scale_factors; + for (size_t i = 0; i < image_reps.size(); ++i) { + scale_factors.push_back(image_reps[i].scale()); + } + std::sort(scale_factors.begin(), scale_factors.end()); + EXPECT_EQ(1.5f, scale_factors[0]); + EXPECT_EQ(2.0f, scale_factors[1]); + + // Requesting 1.75 scale factor also falls back to 2.0f and rescale. + // However, the image already has the 2.0f data, so it won't fetch again. + image.GetRepresentation(1.75f); + EXPECT_EQ(0.0f, source->GetLastRequestedScaleAndReset()); + image_reps = image.image_reps(); + EXPECT_EQ(3u, image_reps.size()); + + scale_factors.clear(); + for (size_t i = 0; i < image_reps.size(); ++i) { + scale_factors.push_back(image_reps[i].scale()); + } + std::sort(scale_factors.begin(), scale_factors.end()); + EXPECT_EQ(1.5f, scale_factors[0]); + EXPECT_EQ(1.75f, scale_factors[1]); + EXPECT_EQ(2.0f, scale_factors[2]); + + // 1.25 is falled back to 1.0. + image.GetRepresentation(1.25f); + EXPECT_EQ(1.0f, source->GetLastRequestedScaleAndReset()); + image_reps = image.image_reps(); + EXPECT_EQ(5u, image_reps.size()); + + // Scale factor less than 1.0f will be falled back to 1.0f + image.GetRepresentation(0.75f); + EXPECT_EQ(0.0f, source->GetLastRequestedScaleAndReset()); + image_reps = image.image_reps(); + EXPECT_EQ(6u, image_reps.size()); + + scale_factors.clear(); + for (size_t i = 0; i < image_reps.size(); ++i) { + scale_factors.push_back(image_reps[i].scale()); + } + std::sort(scale_factors.begin(), scale_factors.end()); + EXPECT_EQ(0.75f, scale_factors[0]); + EXPECT_EQ(1.0f, scale_factors[1]); + EXPECT_EQ(1.25f, scale_factors[2]); + EXPECT_EQ(1.5f, scale_factors[3]); + EXPECT_EQ(1.75f, scale_factors[4]); + EXPECT_EQ(2.0f, scale_factors[5]); + + // Scale factor greater than 2.0f is falled back to 2.0f because it's not + // supported. + image.GetRepresentation(3.0f); + EXPECT_EQ(0.0f, source->GetLastRequestedScaleAndReset()); + image_reps = image.image_reps(); + EXPECT_EQ(7u, image_reps.size()); +} + +TEST_F(ImageSkiaTest, ArbitraryScaleFactorWithMissingResource) { + // Do not test if the ImageSkia doesn't support arbitrary scale factors. + if (!ImageSkia::IsDSFScalingInImageSkiaEnabled()) + return; + + ImageSkia image(new FixedSource( + ImageSkiaRep(Size(100, 200), 1.0f)), Size(100, 200)); + + // Requesting 1.5f -- falls back to 2.0f, but couldn't find. It should + // look up 1.0f and then rescale it. + const ImageSkiaRep& rep = image.GetRepresentation(1.5f); + EXPECT_EQ(1.5f, rep.scale()); + EXPECT_EQ(2U, image.image_reps().size()); + EXPECT_EQ(1.0f, image.image_reps()[0].scale()); + EXPECT_EQ(1.5f, image.image_reps()[1].scale()); +} + +TEST_F(ImageSkiaTest, UnscaledImageForArbitraryScaleFactor) { + // Do not test if the ImageSkia doesn't support arbitrary scale factors. + if (!ImageSkia::IsDSFScalingInImageSkiaEnabled()) + return; + + // 0.0f means unscaled. + ImageSkia image(new FixedSource( + ImageSkiaRep(Size(100, 200), 0.0f)), Size(100, 200)); + + // Requesting 2.0f, which should return 1.0f unscaled image. + const ImageSkiaRep& rep = image.GetRepresentation(2.0f); + EXPECT_EQ(1.0f, rep.scale()); + EXPECT_EQ("100x200", rep.pixel_size().ToString()); + EXPECT_TRUE(rep.unscaled()); + EXPECT_EQ(1U, image.image_reps().size()); + + // Same for any other scale factors. + const ImageSkiaRep& rep15 = image.GetRepresentation(1.5f); + EXPECT_EQ(1.0f, rep15.scale()); + EXPECT_EQ("100x200", rep15.pixel_size().ToString()); + EXPECT_TRUE(rep15.unscaled()); + EXPECT_EQ(1U, image.image_reps().size()); + + const ImageSkiaRep& rep12 = image.GetRepresentation(1.2f); + EXPECT_EQ(1.0f, rep12.scale()); + EXPECT_EQ("100x200", rep12.pixel_size().ToString()); + EXPECT_TRUE(rep12.unscaled()); + EXPECT_EQ(1U, image.image_reps().size()); +} + } // namespace gfx diff --git a/chromium/ui/gfx/image/image_skia_util_mac.h b/chromium/ui/gfx/image/image_skia_util_mac.h index 553c3f8741d..fc6a65fb027 100644 --- a/chromium/ui/gfx/image/image_skia_util_mac.h +++ b/chromium/ui/gfx/image/image_skia_util_mac.h @@ -31,10 +31,6 @@ GFX_EXPORT gfx::ImageSkia ImageSkiaFromNSImage(NSImage* image); GFX_EXPORT gfx::ImageSkia ImageSkiaFromResizedNSImage(NSImage* image, NSSize size); -// Resizes |[NSImage imageNamed:@NSApplicationIcon]| to have edge width of -// |size| DIP and returns result as ImageSkia. -GFX_EXPORT gfx::ImageSkia ApplicationIconAtSize(int size); - // Converts to NSImage from ImageSkia. GFX_EXPORT NSImage* NSImageFromImageSkia(const gfx::ImageSkia& image_skia); diff --git a/chromium/ui/gfx/image/image_skia_util_mac.mm b/chromium/ui/gfx/image/image_skia_util_mac.mm index 281badc7423..26c067331a7 100644 --- a/chromium/ui/gfx/image/image_skia_util_mac.mm +++ b/chromium/ui/gfx/image/image_skia_util_mac.mm @@ -78,12 +78,6 @@ gfx::ImageSkia ImageSkiaFromResizedNSImage(NSImage* image, return image_skia; } -gfx::ImageSkia ApplicationIconAtSize(int desired_size) { - NSImage* image = [NSImage imageNamed:@"NSApplicationIcon"]; - return ImageSkiaFromResizedNSImage(image, - NSMakeSize(desired_size, desired_size)); -} - NSImage* NSImageFromImageSkia(const gfx::ImageSkia& image_skia) { if (image_skia.isNull()) return nil; diff --git a/chromium/ui/gfx/image/image_unittest.cc b/chromium/ui/gfx/image/image_unittest.cc index 039cb13f606..ca9efa0003c 100644 --- a/chromium/ui/gfx/image/image_unittest.cc +++ b/chromium/ui/gfx/image/image_unittest.cc @@ -10,10 +10,7 @@ #include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_unittest_util.h" -#if defined(TOOLKIT_GTK) -#include <gtk/gtk.h> -#include "ui/gfx/gtk_util.h" -#elif defined(OS_IOS) +#if defined(OS_IOS) #include "base/mac/foundation_util.h" #include "skia/ext/skia_utils_ios.h" #elif defined(OS_MACOSX) @@ -75,7 +72,7 @@ TEST_F(ImageTest, EmptyImage) { // Test constructing a gfx::Image from an empty PlatformImage. TEST_F(ImageTest, EmptyImageFromEmptyPlatformImage) { -#if defined(OS_IOS) || defined(OS_MACOSX) || defined(TOOLKIT_GTK) +#if defined(OS_IOS) || defined(OS_MACOSX) gfx::Image image1(NULL); EXPECT_TRUE(image1.IsEmpty()); EXPECT_EQ(0, image1.Width()); @@ -246,12 +243,24 @@ TEST_F(ImageTest, MultiResolutionPNGToImageSkia) { scales.push_back(1.0f); scales.push_back(2.0f); gfx::ImageSkia image_skia = image.AsImageSkia(); - EXPECT_TRUE(gt::ImageSkiaStructureMatches(image_skia, kSize1x, kSize1x, - scales)); EXPECT_TRUE(gt::IsEqual(bytes1x, image_skia.GetRepresentation(1.0f).sk_bitmap())); EXPECT_TRUE(gt::IsEqual(bytes2x, image_skia.GetRepresentation(2.0f).sk_bitmap())); + EXPECT_TRUE(gt::ImageSkiaStructureMatches(image_skia, kSize1x, kSize1x, + scales)); +#if !defined(OS_IOS) + // IOS does not support arbitrary scale factors. + gfx::ImageSkiaRep rep_1_6x = image_skia.GetRepresentation(1.6f); + ASSERT_FALSE(rep_1_6x.is_null()); + ASSERT_EQ(1.6f, rep_1_6x.scale()); + EXPECT_EQ("40x40", rep_1_6x.pixel_size().ToString()); + + gfx::ImageSkiaRep rep_0_8x = image_skia.GetRepresentation(0.8f); + ASSERT_FALSE(rep_0_8x.is_null()); + ASSERT_EQ(0.8f, rep_0_8x.scale()); + EXPECT_EQ("20x20", rep_0_8x.pixel_size().ToString()); +#endif } TEST_F(ImageTest, MultiResolutionPNGToPlatform) { @@ -436,27 +445,6 @@ TEST_F(ImageTest, PlatformToSkiaToCopy) { delete bitmap; } -#if defined(TOOLKIT_GTK) -TEST_F(ImageTest, SkiaToGdkCopy) { - GdkPixbuf* pixbuf; - - { - gfx::Image image(gt::CreateImageSkia(25, 25)); - pixbuf = image.CopyGdkPixbuf(); - } - - EXPECT_TRUE(pixbuf); - g_object_unref(pixbuf); -} - -TEST_F(ImageTest, SkiaToCairoCreatesGdk) { - gfx::Image image(gt::CreateImageSkia(25, 25)); - EXPECT_FALSE(image.HasRepresentation(gfx::Image::kImageRepGdk)); - EXPECT_TRUE(image.ToCairo()); - EXPECT_TRUE(image.HasRepresentation(gfx::Image::kImageRepGdk)); -} -#endif - #if defined(OS_IOS) TEST_F(ImageTest, SkiaToCocoaTouchCopy) { UIImage* ui_image; @@ -495,9 +483,8 @@ TEST_F(ImageTest, SkBitmapConversionPreservesOrientation) { const int width = 50; const int height = 50; SkBitmap bitmap; - bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); - bitmap.allocPixels(); - bitmap.eraseRGB(0, 255, 0); + bitmap.allocN32Pixels(width, height); + bitmap.eraseARGB(255, 0, 255, 0); // Paint the upper half of the image in red (lower half is in green). SkCanvas canvas(bitmap); @@ -538,9 +525,7 @@ TEST_F(ImageTest, SkBitmapConversionPreservesTransparency) { const int width = 50; const int height = 50; SkBitmap bitmap; - bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0, - kPremul_SkAlphaType); - bitmap.allocPixels(); + bitmap.allocN32Pixels(width, height); bitmap.eraseARGB(0, 0, 255, 0); // Paint the upper half of the image in red (lower half is transparent). diff --git a/chromium/ui/gfx/image/image_unittest_util.cc b/chromium/ui/gfx/image/image_unittest_util.cc index 4baf30a4cc6..e9a003216d9 100644 --- a/chromium/ui/gfx/image/image_unittest_util.cc +++ b/chromium/ui/gfx/image/image_unittest_util.cc @@ -15,10 +15,7 @@ #include "ui/gfx/codec/png_codec.h" #include "ui/gfx/image/image_skia.h" -#if defined(TOOLKIT_GTK) -#include <gtk/gtk.h> -#include "ui/gfx/gtk_util.h" -#elif defined(OS_IOS) +#if defined(OS_IOS) #include "base/mac/foundation_util.h" #include "base/mac/scoped_cftyperef.h" #include "skia/ext/skia_utils_ios.h" @@ -59,7 +56,7 @@ const SkBitmap CreateBitmap(int width, int height) { SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); bitmap.allocPixels(); - bitmap.eraseRGB(0, 255, 0); + bitmap.eraseARGB(255, 0, 255, 0); return bitmap; } @@ -83,6 +80,8 @@ gfx::Image CreateImage(int width, int height) { } bool IsEqual(const gfx::Image& img1, const gfx::Image& img2) { + img1.AsImageSkia().EnsureRepsForSupportedScales(); + img2.AsImageSkia().EnsureRepsForSupportedScales(); std::vector<gfx::ImageSkiaRep> img1_reps = img1.AsImageSkia().image_reps(); gfx::ImageSkia image_skia2 = img2.AsImageSkia(); if (image_skia2.image_reps().size() != img1_reps.size()) @@ -192,8 +191,6 @@ PlatformImage CreatePlatformImage() { NSImage* image = gfx::SkBitmapToNSImage(bitmap); base::mac::NSObjectRetain(image); return image; -#elif defined(TOOLKIT_GTK) - return gfx::GdkPixbufFromSkBitmap(bitmap); #else return gfx::ImageSkia::CreateFrom1xBitmap(bitmap); #endif @@ -204,8 +201,6 @@ gfx::Image::RepresentationType GetPlatformRepresentationType() { return gfx::Image::kImageRepCocoaTouch; #elif defined(OS_MACOSX) return gfx::Image::kImageRepCocoa; -#elif defined(TOOLKIT_GTK) - return gfx::Image::kImageRepGdk; #else return gfx::Image::kImageRepSkia; #endif @@ -216,8 +211,6 @@ PlatformImage ToPlatformType(const gfx::Image& image) { return image.ToUIImage(); #elif defined(OS_MACOSX) return image.ToNSImage(); -#elif defined(TOOLKIT_GTK) - return image.ToGdkPixbuf(); #else return image.AsImageSkia(); #endif @@ -228,8 +221,6 @@ PlatformImage CopyPlatformType(const gfx::Image& image) { return image.CopyUIImage(); #elif defined(OS_MACOSX) return image.CopyNSImage(); -#elif defined(TOOLKIT_GTK) - return image.CopyGdkPixbuf(); #else return image.AsImageSkia(); #endif @@ -237,16 +228,6 @@ PlatformImage CopyPlatformType(const gfx::Image& image) { #if defined(OS_MACOSX) // Defined in image_unittest_util_mac.mm. -#elif defined(TOOLKIT_GTK) -SkColor GetPlatformImageColor(PlatformImage image, int x, int y) { - int n_channels = gdk_pixbuf_get_n_channels(image); - int rowstride = gdk_pixbuf_get_rowstride(image); - guchar* gdk_pixels = gdk_pixbuf_get_pixels(image); - - guchar* pixel = gdk_pixels + (y * rowstride) + (x * n_channels); - guchar alpha = gdk_pixbuf_get_has_alpha(image) ? pixel[3] : 255; - return SkColorSetARGB(alpha, pixel[0], pixel[1], pixel[2]); -} #else SkColor GetPlatformImageColor(PlatformImage image, int x, int y) { SkBitmap bitmap = *image.bitmap(); @@ -264,7 +245,7 @@ void CheckIsTransparent(SkColor color) { } bool IsPlatformImageValid(PlatformImage image) { -#if defined(OS_MACOSX) || defined(TOOLKIT_GTK) +#if defined(OS_MACOSX) return image != NULL; #else return !image.isNull(); @@ -272,7 +253,7 @@ bool IsPlatformImageValid(PlatformImage image) { } bool PlatformImagesEqual(PlatformImage image1, PlatformImage image2) { -#if defined(OS_MACOSX) || defined(TOOLKIT_GTK) +#if defined(OS_MACOSX) return image1 == image2; #else return image1.BackedBySameObjectAs(image2); diff --git a/chromium/ui/gfx/image/image_unittest_util.h b/chromium/ui/gfx/image/image_unittest_util.h index 4788e4e1009..cac8015eb16 100644 --- a/chromium/ui/gfx/image/image_unittest_util.h +++ b/chromium/ui/gfx/image/image_unittest_util.h @@ -18,8 +18,6 @@ namespace test { typedef UIImage* PlatformImage; #elif defined(OS_MACOSX) typedef NSImage* PlatformImage; -#elif defined(TOOLKIT_GTK) -typedef GdkPixbuf* PlatformImage; #else typedef gfx::ImageSkia PlatformImage; #endif diff --git a/chromium/ui/gfx/image/image_util.cc b/chromium/ui/gfx/image/image_util.cc index 59d631d171d..89a3f8c03b8 100644 --- a/chromium/ui/gfx/image/image_util.cc +++ b/chromium/ui/gfx/image/image_util.cc @@ -12,6 +12,8 @@ namespace gfx { +const uint32_t kMinimumVisibleOpacity = 12; + // The iOS implementations of the JPEG functions are in image_util_ios.mm. #if !defined(OS_IOS) Image ImageFrom1xJPEGEncodedData(const unsigned char* input, @@ -24,7 +26,7 @@ Image ImageFrom1xJPEGEncodedData(const unsigned char* input, } bool JPEG1xEncodedDataFromImage(const Image& image, int quality, - std::vector<unsigned char>* dst) { + std::vector<unsigned char>* dst) { const gfx::ImageSkiaRep& image_skia_rep = image.AsImageSkia().GetRepresentation(1.0f); if (image_skia_rep.scale() != 1.0f) @@ -45,4 +47,57 @@ bool JPEG1xEncodedDataFromImage(const Image& image, int quality, } #endif // !defined(OS_IOS) +bool VisibleMargins(const ImageSkia& image, int* leading, int* trailing) { + *leading = 0; + *trailing = std::max(1, image.width()) - 1; + if (!image.HasRepresentation(1.0)) + return false; + + const ImageSkiaRep& rep = image.GetRepresentation(1.0); + if (rep.is_null()) + return false; + + const SkBitmap& bitmap = rep.sk_bitmap(); + if (bitmap.isNull() || bitmap.width() == 0) + return false; + + if (bitmap.isOpaque()) + return true; + + SkAutoLockPixels l(bitmap); + int inner_min = bitmap.width(); + for (int x = 0; x < bitmap.width(); ++x) { + for (int y = 0; y < bitmap.height(); ++y) { + if (SkColorGetA(bitmap.getColor(x, y)) > kMinimumVisibleOpacity) { + inner_min = x; + break; + } + } + if (inner_min < bitmap.width()) + break; + } + + int inner_max = -1; + for (int x = bitmap.width() - 1; x > inner_min; --x) { + for (int y = 0; y < bitmap.height(); ++y) { + if (SkColorGetA(bitmap.getColor(x, y)) > kMinimumVisibleOpacity) { + inner_max = x; + break; + } + } + if (inner_max > -1) + break; + } + + if (inner_min == bitmap.width()) { + *leading = bitmap.width()/2; + *trailing = bitmap.width()/2 + 1; + return true; + } + + *leading = inner_min; + *trailing = inner_max; + return true; } + +} // namespace gfx diff --git a/chromium/ui/gfx/image/image_util.h b/chromium/ui/gfx/image/image_util.h index f33835ca232..2711850dc96 100644 --- a/chromium/ui/gfx/image/image_util.h +++ b/chromium/ui/gfx/image/image_util.h @@ -12,6 +12,7 @@ namespace gfx { class Image; +class ImageSkia; } namespace gfx { @@ -31,6 +32,22 @@ GFX_EXPORT bool JPEG1xEncodedDataFromImage(const Image& image, int quality, std::vector<unsigned char>* dst); +// Returns the visible (non-transparent) width of the 1x rep of the given +// image. If the image has no transparency, the leading value will be 0 and +// the trailing will be the image width. Return values are in the 1x width +// pixel units. Margins are given in 0-based column format. So if the image +// has only transparent pixels in columns 0, 1, 2, 3, then the leading value +// will be 4. Similarly, if there are all transparent pixels in column +// width-2, width-1, then the trailing margin value will be width-3. +// Returns true if the value is computed from opacity, false if it is a +// default value because of null image, missing Rep, etc. +// This method is only suitable for fairly small images (i.e. 16x16). +// The margins for a completely transparent image will be w/2-1, w/2, but this +// will be an expensive operation: it isn't expected that it will be frequently +// calculated. +GFX_EXPORT bool VisibleMargins(const ImageSkia& image, + int* leading, int* trailing); + } // namespace gfx #endif // UI_GFX_IMAGE_IMAGE_UTIL_H_ diff --git a/chromium/ui/gfx/image/image_util_unittest.cc b/chromium/ui/gfx/image/image_util_unittest.cc index cd9d74898f6..74c39e68fa4 100644 --- a/chromium/ui/gfx/image/image_util_unittest.cc +++ b/chromium/ui/gfx/image/image_util_unittest.cc @@ -9,6 +9,8 @@ #include "base/memory/scoped_ptr.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkRect.h" +#include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_unittest_util.h" TEST(ImageUtilTest, JPEGEncodeAndDecode) { @@ -23,3 +25,79 @@ TEST(ImageUtilTest, JPEGEncodeAndDecode) { // JPEG is lossy, so simply check that the image decoded successfully. EXPECT_FALSE(decoded.IsEmpty()); } + +TEST(ImageUtilTest, TestVisibleMargins) { + // Image with non-transparent piece should return margins at those + // columns. + SkBitmap bitmap1; + bitmap1.setConfig(SkBitmap::kARGB_8888_Config, 16, 16); + bitmap1.allocPixels(); + bitmap1.eraseColor(SK_ColorTRANSPARENT); + bitmap1.eraseArea(SkIRect::MakeLTRB(3, 3, 14, 14), SK_ColorYELLOW); + gfx::ImageSkia img = gfx::ImageSkia::CreateFrom1xBitmap(bitmap1); + int x = 0; + int y = 0; + gfx::VisibleMargins(img, &x, &y); + EXPECT_EQ(3, x); + EXPECT_EQ(13, y); + EXPECT_EQ(16, img.width()); + + // Full-width-transparent image should return margins in the center + // of the image. + SkBitmap bitmap2; + bitmap2.setConfig(SkBitmap::kARGB_8888_Config, 16, 16); + bitmap2.allocPixels(); + bitmap2.eraseColor(SK_ColorTRANSPARENT); + gfx::ImageSkia img_transparent = gfx::ImageSkia::CreateFrom1xBitmap(bitmap2); + x = 0; + y = 0; + gfx::VisibleMargins(img_transparent, &x, &y); + EXPECT_EQ(8, x); + EXPECT_EQ(9, y); + EXPECT_EQ(16, img_transparent.width()); + + // Image with non-transparent piece that is skewed to one side should + // return margins at those columns. + SkBitmap bitmap3; + bitmap3.setConfig(SkBitmap::kARGB_8888_Config, 16, 16); + bitmap3.allocPixels(); + bitmap3.eraseColor(SK_ColorTRANSPARENT); + bitmap3.eraseArea(SkIRect::MakeLTRB(3, 3, 5, 5), SK_ColorYELLOW); + gfx::ImageSkia img3 = gfx::ImageSkia::CreateFrom1xBitmap(bitmap3); + x = 0; + y = 0; + gfx::VisibleMargins(img3, &x, &y); + EXPECT_EQ(3, x); + EXPECT_EQ(4, y); + EXPECT_EQ(16, img3.width()); + + // Image with non-transparent piece that is at one edge should + // return margins at those columns. + SkBitmap bitmap4; + bitmap4.setConfig(SkBitmap::kARGB_8888_Config, 16, 16); + bitmap4.allocPixels(); + bitmap4.eraseColor(SK_ColorTRANSPARENT); + bitmap4.eraseArea(SkIRect::MakeLTRB(0, 3, 5, 5), SK_ColorYELLOW); + gfx::ImageSkia img4 = gfx::ImageSkia::CreateFrom1xBitmap(bitmap4); + x = 0; + y = 0; + gfx::VisibleMargins(img4, &x, &y); + EXPECT_EQ(0, x); + EXPECT_EQ(4, y); + EXPECT_EQ(16, img4.width()); + + // Image with non-transparent piece that is at trailing edge should + // return margins at those columns. + SkBitmap bitmap5; + bitmap5.setConfig(SkBitmap::kARGB_8888_Config, 16, 16); + bitmap5.allocPixels(); + bitmap5.eraseColor(SK_ColorTRANSPARENT); + bitmap5.eraseArea(SkIRect::MakeLTRB(4, 3, 16, 16), SK_ColorYELLOW); + gfx::ImageSkia img5 = gfx::ImageSkia::CreateFrom1xBitmap(bitmap5); + x = 0; + y = 0; + gfx::VisibleMargins(img5, &x, &y); + EXPECT_EQ(4, x); + EXPECT_EQ(15, y); + EXPECT_EQ(16, img5.width()); +} |