summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpkotwicz@chromium.org <pkotwicz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-10 17:41:13 +0000
committerpkotwicz@chromium.org <pkotwicz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-10 17:41:13 +0000
commit89753fdf570d25d44fe9d5f01600fa5138ca1342 (patch)
treebe9814b040e47a28f25578073baaf3aacf673f78
parent8bd4a46da03767be3a2fa16c9fc32ed2233e47f7 (diff)
downloadchromium_src-89753fdf570d25d44fe9d5f01600fa5138ca1342.zip
chromium_src-89753fdf570d25d44fe9d5f01600fa5138ca1342.tar.gz
chromium_src-89753fdf570d25d44fe9d5f01600fa5138ca1342.tar.bz2
This patch makes ImageSkia more like SkBitmap. The goal is to make swapping from SkBitmap to ImageSkia easier.
Notable changes: - ImageSkia can be cheaply copied - Added extractSubset, will remove after SkBitmaps have been converted to ImageSkia - Modified API to look more like SkBitmap Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=136304 Review URL: https://chromiumcodereview.appspot.com/10245003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@136332 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/image_loading_tracker.cc9
-rw-r--r--chrome/browser/extensions/image_loading_tracker_unittest.cc7
-rw-r--r--chrome/browser/themes/browser_theme_pack.cc41
-rw-r--r--chrome/browser/web_applications/web_app_mac.mm5
-rw-r--r--skia/ext/skia_utils_mac.h10
-rw-r--r--skia/ext/skia_utils_mac.mm46
-rw-r--r--skia/ext/skia_utils_mac_unittest.mm40
-rw-r--r--ui/base/resource/resource_bundle.cc22
-rw-r--r--ui/gfx/canvas.cc107
-rw-r--r--ui/gfx/canvas.h56
-rw-r--r--ui/gfx/image/image.cc35
-rw-r--r--ui/gfx/image/image.h8
-rw-r--r--ui/gfx/image/image_mac.mm37
-rw-r--r--ui/gfx/image/image_mac_unittest.mm96
-rw-r--r--ui/gfx/image/image_skia.cc255
-rw-r--r--ui/gfx/image/image_skia.h155
-rw-r--r--ui/gfx/image/image_unittest.cc66
17 files changed, 577 insertions, 418 deletions
diff --git a/chrome/browser/extensions/image_loading_tracker.cc b/chrome/browser/extensions/image_loading_tracker.cc
index 3bbf4334e..ab43b06 100644
--- a/chrome/browser/extensions/image_loading_tracker.cc
+++ b/chrome/browser/extensions/image_loading_tracker.cc
@@ -18,6 +18,7 @@
#include "skia/ext/image_operations.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/image/image.h"
+#include "ui/gfx/image/image_skia.h"
#include "webkit/glue/image_decoder.h"
using content::BrowserThread;
@@ -303,13 +304,13 @@ void ImageLoadingTracker::OnImageLoaded(
std::string extension_id = info->extension_id;
if (info->bitmaps.size() > 0) {
- std::vector<const SkBitmap*> bitmaps;
+ gfx::ImageSkia image_skia;
for (std::vector<SkBitmap>::const_iterator it = info->bitmaps.begin();
it != info->bitmaps.end(); ++it) {
- // gfx::Image takes ownership of this bitmap.
- bitmaps.push_back(new SkBitmap(*it));
+ // TODO(pkotwicz): Do something better but ONLY when ENABLE_DIP.
+ image_skia.AddBitmapForScale(*it, 1.0f);
}
- image = gfx::Image(bitmaps);
+ image = gfx::Image(image_skia);
}
load_map_.erase(load_map_it);
diff --git a/chrome/browser/extensions/image_loading_tracker_unittest.cc b/chrome/browser/extensions/image_loading_tracker_unittest.cc
index 4306936..edbbcee 100644
--- a/chrome/browser/extensions/image_loading_tracker_unittest.cc
+++ b/chrome/browser/extensions/image_loading_tracker_unittest.cc
@@ -218,11 +218,10 @@ TEST_F(ImageLoadingTrackerTest, MultipleImages) {
EXPECT_EQ(1, image_loaded_count());
// Check that all images were loaded.
- const std::vector<const SkBitmap*>& bitmaps =
- image_.ToImageSkia()->bitmaps();
+ const std::vector<SkBitmap> bitmaps = image_.ToImageSkia()->bitmaps();
ASSERT_EQ(2u, bitmaps.size());
- const SkBitmap* bmp1 = bitmaps[0];
- const SkBitmap* bmp2 = bitmaps[1];
+ const SkBitmap* bmp1 = &bitmaps[0];
+ const SkBitmap* bmp2 = &bitmaps[1];
if (bmp1->width() > bmp2->width()) {
std::swap(bmp1, bmp2);
}
diff --git a/chrome/browser/themes/browser_theme_pack.cc b/chrome/browser/themes/browser_theme_pack.cc
index 0ae0895..b5f17eb 100644
--- a/chrome/browser/themes/browser_theme_pack.cc
+++ b/chrome/browser/themes/browser_theme_pack.cc
@@ -311,15 +311,18 @@ base::RefCountedMemory* ReadFileData(const FilePath& path) {
// the returned image.
gfx::Image* CreateHSLShiftedImage(const gfx::Image& image,
const color_utils::HSL& hsl_shift) {
- const std::vector<const SkBitmap*>& src_bitmaps =
- image.ToImageSkia()->bitmaps();
- std::vector<const SkBitmap*> dst_bitmaps;
+ const gfx::ImageSkia* src_image = image.ToImageSkia();
+ const std::vector<SkBitmap> src_bitmaps = src_image->bitmaps();
+ gfx::ImageSkia dst_image;
for (size_t i = 0; i < src_bitmaps.size(); ++i) {
- const SkBitmap* bitmap = src_bitmaps[i];
- dst_bitmaps.push_back(new SkBitmap(
- SkBitmapOperations::CreateHSLShiftedBitmap(*bitmap, hsl_shift)));
+ const SkBitmap& bitmap = src_bitmaps[i];
+ float scale_factor =
+ static_cast<float>(bitmap.width()) / src_image->width();
+ dst_image.AddBitmapForScale(
+ SkBitmapOperations::CreateHSLShiftedBitmap(bitmap, hsl_shift),
+ scale_factor);
}
- return new gfx::Image(dst_bitmaps);
+ return new gfx::Image(dst_image);
}
} // namespace
@@ -992,32 +995,32 @@ void BrowserThemePack::GenerateTabBackgroundImages(ImageCache* bitmaps) const {
// with a PRS_THEME_FRAME.
ImageCache::const_iterator it = bitmaps->find(prs_base_id);
if (it != bitmaps->end()) {
- const gfx::Image& image_to_tint = *(it->second);
- const std::vector<const SkBitmap*>& bitmaps_to_tint =
- image_to_tint.ToImageSkia()->bitmaps();
- std::vector<const SkBitmap*> tinted_bitmaps;
+ const gfx::ImageSkia* image_to_tint = (it->second)->ToImageSkia();
+ const std::vector<SkBitmap> bitmaps_to_tint = image_to_tint->bitmaps();
+ gfx::ImageSkia tinted_image;
for (size_t j = 0; j < bitmaps_to_tint.size(); ++j) {
SkBitmap bg_tint = SkBitmapOperations::CreateHSLShiftedBitmap(
- *bitmaps_to_tint[j], GetTintInternal(
+ bitmaps_to_tint[j], GetTintInternal(
ThemeService::TINT_BACKGROUND_TAB));
int vertical_offset = bitmaps->count(prs_id)
? kRestoredTabVerticalOffset : 0;
- SkBitmap* bg_tab = new SkBitmap(SkBitmapOperations::CreateTiledBitmap(
- bg_tint, 0, vertical_offset, bg_tint.width(), bg_tint.height()));
+ SkBitmap bg_tab = SkBitmapOperations::CreateTiledBitmap(
+ bg_tint, 0, vertical_offset, bg_tint.width(), bg_tint.height());
// If they've provided a custom image, overlay it.
ImageCache::const_iterator overlay_it = bitmaps->find(prs_id);
if (overlay_it != bitmaps->end()) {
const SkBitmap* overlay = overlay_it->second->ToSkBitmap();
- SkCanvas canvas(*bg_tab);
- for (int x = 0; x < bg_tab->width(); x += overlay->width())
+ SkCanvas canvas(bg_tab);
+ for (int x = 0; x < bg_tab.width(); x += overlay->width())
canvas.drawBitmap(*overlay, static_cast<SkScalar>(x), 0, NULL);
}
- tinted_bitmaps.push_back(bg_tab);
+ float scale_factor =
+ static_cast<float>(bg_tab.width()) / image_to_tint->width();
+ tinted_image.AddBitmapForScale(bg_tab, scale_factor);
}
- gfx::Image* tinted_image = new gfx::Image(tinted_bitmaps);
- temp_output[prs_id] = tinted_image;
+ temp_output[prs_id] = new gfx::Image(tinted_image);
}
}
diff --git a/chrome/browser/web_applications/web_app_mac.mm b/chrome/browser/web_applications/web_app_mac.mm
index bcc7991..46ce5f5 100644
--- a/chrome/browser/web_applications/web_app_mac.mm
+++ b/chrome/browser/web_applications/web_app_mac.mm
@@ -198,10 +198,9 @@ bool WebAppShortcutCreator::UpdateIcon(const FilePath& app_path) const {
scoped_nsobject<IconFamily> icon_family([[IconFamily alloc] init]);
bool image_added = false;
- const std::vector<const SkBitmap*>& bitmaps =
- info_.favicon.ToImageSkia()->bitmaps();
+ const std::vector<SkBitmap> bitmaps = info_.favicon.ToImageSkia()->bitmaps();
for (size_t i = 0; i < bitmaps.size(); ++i) {
- NSBitmapImageRep* image_rep = SkBitmapToImageRep(*bitmaps[i]);
+ NSBitmapImageRep* image_rep = SkBitmapToImageRep(bitmaps[i]);
if (!image_rep)
continue;
diff --git a/skia/ext/skia_utils_mac.h b/skia/ext/skia_utils_mac.h
index da290ea..f8ba28c 100644
--- a/skia/ext/skia_utils_mac.h
+++ b/skia/ext/skia_utils_mac.h
@@ -24,10 +24,12 @@ typedef struct _NSSize NSSize;
#endif
#ifdef __OBJC__
+@class NSBitmapImageRep;
@class NSImage;
@class NSImageRep;
@class NSColor;
#else
+class NSBitmapImageRep;
class NSImage;
class NSImageRep;
class NSColor;
@@ -81,6 +83,10 @@ SK_API SkBitmap NSImageToSkBitmap(NSImage* image, NSSize size, bool is_opaque);
SK_API SkBitmap NSImageRepToSkBitmap(
NSImageRep* image, NSSize size, bool is_opaque);
+// Given an SkBitmap, return an autoreleased NSBitmapImageRep in the generic
+// color space.
+SK_API NSBitmapImageRep* SkBitmapToNSBitmapImageRep(const SkBitmap& image);
+
// Given an SkBitmap and a color space, return an autoreleased NSImage.
SK_API NSImage* SkBitmapToNSImageWithColorSpace(const SkBitmap& icon,
CGColorSpaceRef colorSpace);
@@ -90,10 +96,6 @@ SK_API NSImage* SkBitmapToNSImageWithColorSpace(const SkBitmap& icon,
// TODO(thakis): Remove this -- http://crbug.com/69432
SK_API NSImage* SkBitmapToNSImage(const SkBitmap& icon);
-// Given a vector of SkBitmaps, return an NSImage with each bitmap added
-// as a representation.
-SK_API NSImage* SkBitmapsToNSImage(const std::vector<const SkBitmap*>& bitmaps);
-
// Returns |[NSImage imageNamed:@"NSApplicationIcon"]| as SkBitmap.
SK_API SkBitmap AppplicationIconAtSize(int size);
diff --git a/skia/ext/skia_utils_mac.mm b/skia/ext/skia_utils_mac.mm
index f359089..48070d1 100644
--- a/skia/ext/skia_utils_mac.mm
+++ b/skia/ext/skia_utils_mac.mm
@@ -248,6 +248,20 @@ SkBitmap NSImageRepToSkBitmap(NSImageRep* image, NSSize size, bool is_opaque) {
return NSImageOrNSImageRepToSkBitmap(nil, image, size, is_opaque);
}
+NSBitmapImageRep* SkBitmapToNSBitmapImageRep(const SkBitmap& skiaBitmap) {
+ base::mac::ScopedCFTypeRef<CGColorSpaceRef> color_space(
+ CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB));
+
+ // First convert SkBitmap to CGImageRef.
+ base::mac::ScopedCFTypeRef<CGImageRef> cgimage(
+ SkCreateCGImageRefWithColorspace(skiaBitmap, color_space));
+
+ // Now convert to NSBitmapImageRep.
+ scoped_nsobject<NSBitmapImageRep> bitmap(
+ [[NSBitmapImageRep alloc] initWithCGImage:cgimage]);
+ return [bitmap.release() autorelease];
+}
+
NSImage* SkBitmapToNSImageWithColorSpace(const SkBitmap& skiaBitmap,
CGColorSpaceRef colorSpace) {
if (skiaBitmap.isNull())
@@ -272,43 +286,11 @@ NSImage* SkBitmapToNSImage(const SkBitmap& skiaBitmap) {
return SkBitmapToNSImageWithColorSpace(skiaBitmap, colorSpace.get());
}
-NSImage* SkBitmapsToNSImage(const std::vector<const SkBitmap*>& bitmaps) {
- if (bitmaps.empty())
- return nil;
-
- base::mac::ScopedCFTypeRef<CGColorSpaceRef> color_space(
- CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB));
- scoped_nsobject<NSImage> image([[NSImage alloc] init]);
- NSSize min_size = NSZeroSize;
-
- for (std::vector<const SkBitmap*>::const_iterator it = bitmaps.begin();
- it != bitmaps.end(); ++it) {
- const SkBitmap& skiaBitmap = **it;
- // First convert SkBitmap to CGImageRef.
- base::mac::ScopedCFTypeRef<CGImageRef> cgimage(
- SkCreateCGImageRefWithColorspace(skiaBitmap, color_space));
-
- // Now convert to NSImage.
- scoped_nsobject<NSBitmapImageRep> bitmap(
- [[NSBitmapImageRep alloc] initWithCGImage:cgimage]);
- [image addRepresentation:bitmap];
-
- if (min_size.width == 0 || min_size.width > skiaBitmap.width()) {
- min_size.width = skiaBitmap.width();
- min_size.height = skiaBitmap.height();
- }
- }
-
- [image setSize:min_size];
- return [image.release() autorelease];
-}
-
SkBitmap AppplicationIconAtSize(int size) {
NSImage* image = [NSImage imageNamed:@"NSApplicationIcon"];
return NSImageToSkBitmap(image, NSMakeSize(size, size), /* is_opaque=*/true);
}
-
SkiaBitLocker::SkiaBitLocker(SkCanvas* canvas)
: canvas_(canvas),
cgContext_(0) {
diff --git a/skia/ext/skia_utils_mac_unittest.mm b/skia/ext/skia_utils_mac_unittest.mm
index 24310b0..1353eb6 100644
--- a/skia/ext/skia_utils_mac_unittest.mm
+++ b/skia/ext/skia_utils_mac_unittest.mm
@@ -187,36 +187,16 @@ TEST_F(SkiaUtilsMacTest, FAILS_BitmapToNSImage_BlueRectangle444) {
ShapeHelper(200, 200, false, false);
}
-TEST_F(SkiaUtilsMacTest, FAILS_MultipleBitmapsToNSImage) {
- int redWidth = 10;
- int redHeight = 15;
- int blueWidth = 20;
- int blueHeight = 30;
-
- SkBitmap redBitmap(CreateSkBitmap(redWidth, redHeight, true, true));
- SkBitmap blueBitmap(CreateSkBitmap(blueWidth, blueHeight, false, true));
- std::vector<const SkBitmap*> bitmaps;
- bitmaps.push_back(&redBitmap);
- bitmaps.push_back(&blueBitmap);
-
- NSImage* image = gfx::SkBitmapsToNSImage(bitmaps);
-
- // Image size should be the same as the smallest bitmap.
- EXPECT_DOUBLE_EQ(redWidth, [image size].width);
- EXPECT_DOUBLE_EQ(redHeight, [image size].height);
-
- EXPECT_EQ(2u, [[image representations] count]);
-
- for (NSBitmapImageRep* imageRep in [image representations]) {
- bool isred = [imageRep size].width == redWidth;
- if (isred) {
- EXPECT_DOUBLE_EQ(redHeight, [imageRep size].height);
- } else {
- EXPECT_DOUBLE_EQ(blueWidth, [imageRep size].width);
- EXPECT_DOUBLE_EQ(blueHeight, [imageRep size].height);
- }
- TestImageRep(imageRep, isred);
- }
+TEST_F(SkiaUtilsMacTest, FAILS_BitmapToNSBitmapImageRep_BlueRectangle20x30) {
+ int width = 20;
+ int height = 30;
+
+ SkBitmap bitmap(CreateSkBitmap(width, height, false, true));
+ NSBitmapImageRep* imageRep = gfx::SkBitmapToNSBitmapImageRep(bitmap);
+
+ EXPECT_DOUBLE_EQ(width, [imageRep size].width);
+ EXPECT_DOUBLE_EQ(height, [imageRep size].height);
+ TestImageRep(imageRep, false);
}
TEST_F(SkiaUtilsMacTest, NSImageRepToSkBitmap) {
diff --git a/ui/base/resource/resource_bundle.cc b/ui/base/resource/resource_bundle.cc
index 58765b1..d6a69bd 100644
--- a/ui/base/resource/resource_bundle.cc
+++ b/ui/base/resource/resource_bundle.cc
@@ -24,6 +24,7 @@
#include "ui/base/ui_base_switches.h"
#include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/image/image_skia.h"
namespace ui {
@@ -226,25 +227,26 @@ gfx::Image& ResourceBundle::GetImageNamed(int resource_id) {
if (image.IsEmpty()) {
DCHECK(!delegate_ && !data_packs_.empty()) <<
"Missing call to SetResourcesDataDLL?";
- ScopedVector<const SkBitmap> bitmaps;
+ gfx::ImageSkia image_skia;
for (size_t i = 0; i < data_packs_.size(); ++i) {
- SkBitmap* bitmap = LoadBitmap(*data_packs_[i], resource_id);
- if (bitmap)
- bitmaps.push_back(bitmap);
+ scoped_ptr<SkBitmap> bitmap(LoadBitmap(*data_packs_[i], resource_id));
+ if (bitmap.get()) {
+#if defined(ENABLE_DIP)
+ image_skia.AddBitmapForScale(*bitmap, data_packs_[i]->GetScaleFactor());
+#else
+ image_skia.AddBitmapForScale(*bitmap, 1.0f);
+#endif
+ }
}
- if (bitmaps.empty()) {
+ if (image_skia.empty()) {
LOG(WARNING) << "Unable to load image with id " << resource_id;
NOTREACHED(); // Want to assert in debug mode.
// The load failed to retrieve the image; show a debugging red square.
return GetEmptyImage();
}
- std::vector<const SkBitmap*> tmp_bitmaps;
- bitmaps.release(&tmp_bitmaps);
-
- // Takes ownership of bitmaps.
- image = gfx::Image(tmp_bitmaps);
+ image = gfx::Image(image_skia);
}
// The load was successful, so cache the image.
diff --git a/ui/gfx/canvas.cc b/ui/gfx/canvas.cc
index 288d192..aeabc12 100644
--- a/ui/gfx/canvas.cc
+++ b/ui/gfx/canvas.cc
@@ -254,42 +254,42 @@ void Canvas::DrawFocusRect(const gfx::Rect& rect) {
DrawDashedRect(rect, SK_ColorGRAY);
}
-void Canvas::DrawBitmapInt(const SkBitmap& bitmap, int x, int y) {
- canvas_->drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y));
+void Canvas::DrawBitmapInt(const gfx::ImageSkia& image, int x, int y) {
+ SkPaint paint;
+ DrawBitmapInt(image, x, y, paint);
}
-void Canvas::DrawBitmapInt(const SkBitmap& bitmap,
+void Canvas::DrawBitmapInt(const gfx::ImageSkia& image,
int x, int y,
const SkPaint& paint) {
- canvas_->drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y), &paint);
+ float bitmap_scale;
+ const SkBitmap& bitmap = GetBitmapToPaint(image, &bitmap_scale);
+ if (bitmap.isNull())
+ return;
+
+ canvas_->save();
+ canvas_->scale(SkFloatToScalar(1.0f / bitmap_scale),
+ SkFloatToScalar(1.0f / bitmap_scale));
+ canvas_->drawBitmap(bitmap,
+ SkFloatToScalar(x * bitmap_scale),
+ SkFloatToScalar(y * bitmap_scale));
+ canvas_->restore();
}
-void Canvas::DrawBitmapInt(const SkBitmap& bitmap,
+void Canvas::DrawBitmapInt(const gfx::ImageSkia& image,
int src_x, int src_y, int src_w, int src_h,
int dest_x, int dest_y, int dest_w, int dest_h,
bool filter) {
SkPaint p;
- DrawBitmapInt(bitmap, src_x, src_y, src_w, src_h, dest_x, dest_y,
+ DrawBitmapInt(image, src_x, src_y, src_w, src_h, dest_x, dest_y,
dest_w, dest_h, filter, p);
}
-void Canvas::DrawBitmapInt(const SkBitmap& bitmap,
+void Canvas::DrawBitmapInt(const gfx::ImageSkia& image,
int src_x, int src_y, int src_w, int src_h,
int dest_x, int dest_y, int dest_w, int dest_h,
bool filter,
const SkPaint& paint) {
- DrawBitmapFloat(bitmap, static_cast<float>(src_x), static_cast<float>(src_y),
- static_cast<float>(src_w), static_cast<float>(src_h),
- static_cast<float>(dest_x), static_cast<float>(dest_y),
- static_cast<float>(dest_w), static_cast<float>(dest_h),
- filter, paint);
-}
-
-void Canvas::DrawBitmapFloat(const SkBitmap& bitmap,
- float src_x, float src_y, float src_w, float src_h,
- float dest_x, float dest_y, float dest_w, float dest_h,
- bool filter,
- const SkPaint& paint) {
DLOG_ASSERT(src_x + src_w < std::numeric_limits<int16_t>::max() &&
src_y + src_h < std::numeric_limits<int16_t>::max());
if (src_w <= 0 || src_h <= 0) {
@@ -300,12 +300,22 @@ void Canvas::DrawBitmapFloat(const SkBitmap& bitmap,
if (!IntersectsClipRectInt(dest_x, dest_y, dest_w, dest_h))
return;
- SkRect dest_rect = { SkFloatToScalar(dest_x),
- SkFloatToScalar(dest_y),
- SkFloatToScalar(dest_x + dest_w),
- SkFloatToScalar(dest_y + dest_h) };
+ float user_scale_x = static_cast<float>(dest_w) / src_w;
+ float user_scale_y = static_cast<float>(dest_h) / src_h;
- if (src_w == dest_w && src_h == dest_h) {
+ float bitmap_scale;
+ const SkBitmap& bitmap = GetBitmapToPaint(image, user_scale_x, user_scale_y,
+ &bitmap_scale);
+ if (bitmap.isNull())
+ return;
+
+ SkRect dest_rect = { SkIntToScalar(dest_x),
+ SkIntToScalar(dest_y),
+ SkIntToScalar(dest_x + dest_w),
+ SkIntToScalar(dest_y + dest_h) };
+
+ if (src_w == dest_w && src_h == dest_h &&
+ bitmap_scale == 1.0f && bitmap_scale == 1.0f) {
// Workaround for apparent bug in Skia that causes image to occasionally
// shift.
SkIRect src_rect = { src_x, src_y, src_x + src_w, src_y + src_h };
@@ -321,10 +331,13 @@ void Canvas::DrawBitmapFloat(const SkBitmap& bitmap,
SkShader::kRepeat_TileMode,
SkShader::kRepeat_TileMode);
SkMatrix shader_scale;
- shader_scale.setScale(SkFloatToScalar(dest_w / src_w),
- SkFloatToScalar(dest_h / src_h));
- shader_scale.preTranslate(SkFloatToScalar(-src_x), SkFloatToScalar(-src_y));
- shader_scale.postTranslate(SkFloatToScalar(dest_x), SkFloatToScalar(dest_y));
+ shader_scale.setScale(SkFloatToScalar(user_scale_x),
+ SkFloatToScalar(user_scale_y));
+ shader_scale.preTranslate(SkFloatToScalar(-src_x * bitmap_scale),
+ SkFloatToScalar(-src_y * bitmap_scale));
+ shader_scale.postTranslate(SkFloatToScalar(dest_x * bitmap_scale),
+ SkFloatToScalar(dest_y * bitmap_scale));
+ shader_scale.postScale(1.0f / bitmap_scale, 1.0f / bitmap_scale);
shader->setLocalMatrix(shader_scale);
// Set up our paint to use the shader & release our reference (now just owned
@@ -366,17 +379,22 @@ void Canvas::DrawStringInt(const string16& text,
std::vector<ShadowValue>());
}
-void Canvas::TileImageInt(const SkBitmap& bitmap,
+void Canvas::TileImageInt(const gfx::ImageSkia& image,
int x, int y, int w, int h) {
- TileImageInt(bitmap, 0, 0, x, y, w, h);
+ TileImageInt(image, 0, 0, x, y, w, h);
}
-void Canvas::TileImageInt(const SkBitmap& bitmap,
+void Canvas::TileImageInt(const gfx::ImageSkia& image,
int src_x, int src_y,
int dest_x, int dest_y, int w, int h) {
if (!IntersectsClipRectInt(dest_x, dest_y, w, h))
return;
+ float bitmap_scale;
+ const SkBitmap& bitmap = GetBitmapToPaint(image, &bitmap_scale);
+ if (bitmap.isNull())
+ return;
+
SkPaint paint;
SkShader* shader = SkShader::CreateBitmapShader(bitmap,
@@ -392,6 +410,8 @@ void Canvas::TileImageInt(const SkBitmap& bitmap,
canvas_->translate(SkIntToScalar(dest_x - src_x),
SkIntToScalar(dest_y - src_y));
ClipRect(gfx::Rect(src_x, src_y, w, h));
+ canvas_->scale(SkFloatToScalar(1.0f / bitmap_scale),
+ SkFloatToScalar(1.0f / bitmap_scale));
canvas_->drawPaint(paint);
canvas_->restore();
}
@@ -420,4 +440,29 @@ bool Canvas::IntersectsClipRect(const gfx::Rect& rect) {
rect.width(), rect.height());
}
+
+const SkBitmap& Canvas::GetBitmapToPaint(const gfx::ImageSkia& image,
+ float* bitmap_scale_factor) const {
+ return GetBitmapToPaint(image, 1.0f, 1.0f, bitmap_scale_factor);
+}
+
+const SkBitmap& Canvas::GetBitmapToPaint(const gfx::ImageSkia& image,
+ float user_additional_scale_x,
+ float user_additional_scale_y,
+ float* bitmap_scale_factor) const {
+ SkMatrix m = canvas_->getTotalMatrix();
+ float scale_x = SkScalarToFloat(SkScalarAbs(m.getScaleX())) *
+ user_additional_scale_x;
+ float scale_y = SkScalarToFloat(SkScalarAbs(m.getScaleY())) *
+ user_additional_scale_y;
+
+ const SkBitmap& bitmap = image.GetBitmapForScale(scale_x, scale_y,
+ bitmap_scale_factor);
+ if (!bitmap.isNull() &&
+ (scale_x < *bitmap_scale_factor || scale_y < *bitmap_scale_factor))
+ const_cast<SkBitmap&>(bitmap).buildMipMap();
+
+ return bitmap;
+}
+
} // namespace gfx
diff --git a/ui/gfx/canvas.h b/ui/gfx/canvas.h
index 7653f39..69f402b 100644
--- a/ui/gfx/canvas.h
+++ b/ui/gfx/canvas.h
@@ -12,6 +12,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/string16.h"
#include "skia/ext/platform_canvas.h"
+#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/native_widget_types.h"
class SkBitmap;
@@ -240,47 +241,43 @@ class UI_EXPORT Canvas {
// Draws the given path using the given |paint| parameters.
void DrawPath(const SkPath& path, const SkPaint& paint);
- // Draws a bitmap with the origin at the specified location. The upper left
+ // Draws an image with the origin at the specified location. The upper left
// corner of the bitmap is rendered at the specified location.
- void DrawBitmapInt(const SkBitmap& bitmap, int x, int y);
+ // Parameters are specified relative to current canvas scale not in pixels.
+ // Thus, |x| is 2 pixels if canvas scale = 2 & |x| = 1.
+ void DrawBitmapInt(const gfx::ImageSkia&, int x, int y);
- // Draws a bitmap with the origin at the specified location, using the
+ // Draws an image with the origin at the specified location, using the
// specified paint. The upper left corner of the bitmap is rendered at the
// specified location.
- void DrawBitmapInt(const SkBitmap& bitmap,
+ // Parameters are specified relative to current canvas scale not in pixels.
+ // Thus, |x| is 2 pixels if canvas scale = 2 & |x| = 1.
+ void DrawBitmapInt(const gfx::ImageSkia& image,
int x, int y,
const SkPaint& paint);
- // Draws a portion of a bitmap in the specified location. The src parameters
+ // Draws a portion of an image in the specified location. The src parameters
// correspond to the region of the bitmap to draw in the region defined
// by the dest coordinates.
//
// If the width or height of the source differs from that of the destination,
- // the bitmap will be scaled. When scaling down, it is highly recommended
- // that you call buildMipMap(false) on your bitmap to ensure that it has
- // a mipmap, which will result in much higher-quality output. Set |filter|
- // to use filtering for bitmaps, otherwise the nearest-neighbor algorithm
- // is used for resampling.
+ // the image will be scaled. When scaling down, a mipmap will be generated.
+ // Set |filter| to use filtering for images, otherwise the nearest-neighbor
+ // algorithm is used for resampling.
//
// An optional custom SkPaint can be provided.
- void DrawBitmapInt(const SkBitmap& bitmap,
+ // Parameters are specified relative to current canvas scale not in pixels.
+ // Thus, |x| is 2 pixels if canvas scale = 2 & |x| = 1.
+ void DrawBitmapInt(const gfx::ImageSkia& image,
int src_x, int src_y, int src_w, int src_h,
int dest_x, int dest_y, int dest_w, int dest_h,
bool filter);
- void DrawBitmapInt(const SkBitmap& bitmap,
+ void DrawBitmapInt(const gfx::ImageSkia& image,
int src_x, int src_y, int src_w, int src_h,
int dest_x, int dest_y, int dest_w, int dest_h,
bool filter,
const SkPaint& paint);
- // TODO(pkotwicz): make this function private once gfx::ImageSkia stops
- // calling this method.
- void DrawBitmapFloat(const SkBitmap& bitmap,
- float src_x, float src_y, float src_w, float src_h,
- float dest_x, float dest_y, float dest_w, float dest_h,
- bool filter,
- const SkPaint& paint);
-
// Draws text with the specified color, font and location. The text is
// aligned to the left, vertically centered, clipped to the region. If the
// text is too big, it is truncated and '...' is added to the end.
@@ -315,9 +312,11 @@ class UI_EXPORT Canvas {
void DrawFocusRect(const gfx::Rect& rect);
// Tiles the image in the specified region.
- void TileImageInt(const SkBitmap& bitmap,
+ // Parameters are specified relative to current canvas scale not in pixels.
+ // Thus, |x| is 2 pixels if canvas scale = 2 & |x| = 1.
+ void TileImageInt(const gfx::ImageSkia& image,
int x, int y, int w, int h);
- void TileImageInt(const SkBitmap& bitmap,
+ void TileImageInt(const gfx::ImageSkia& image,
int src_x, int src_y,
int dest_x, int dest_y, int w, int h);
@@ -354,6 +353,19 @@ class UI_EXPORT Canvas {
bool IntersectsClipRectInt(int x, int y, int w, int h);
bool IntersectsClipRect(const gfx::Rect& rect);
+ // Returns the bitmap whose density best matches the current canvas scale.
+ // Returns a null bitmap if |image| contains no bitmaps.
+ // |bitmap_scale_factor| is set to the scale factor of the returned bitmap.
+ // Builds mip map for returned bitmap if necessary.
+ //
+ // An optional additional user defined scale can be provided.
+ const SkBitmap& GetBitmapToPaint(const gfx::ImageSkia& image,
+ float* bitmap_scale_factor) const;
+ const SkBitmap& GetBitmapToPaint(const gfx::ImageSkia& image,
+ float user_defined_scale_factor_x,
+ float user_defined_scale_factor_y,
+ float* bitmap_scale_factor) const;
+
#if defined(OS_WIN)
// Draws text with the specified color, font and location. The text is
// aligned to the left, vertically centered, clipped to the region. If the
diff --git a/ui/gfx/image/image.cc b/ui/gfx/image/image.cc
index e748dd9..dac2ccc 100644
--- a/ui/gfx/image/image.cc
+++ b/ui/gfx/image/image.cc
@@ -31,7 +31,8 @@ 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.
-bool NSImageToSkBitmaps(NSImage* image, std::vector<const SkBitmap*>* bitmaps);
+ImageSkia NSImageToImageSkia(NSImage* image);
+NSImage* ImageSkiaToNSImage(const ImageSkia* image);
#endif
#if defined(TOOLKIT_GTK)
@@ -97,6 +98,7 @@ class ImageRep {
class ImageRepSkia : public ImageRep {
public:
+ // Takes ownership of |image|.
explicit ImageRepSkia(ImageSkia* image)
: ImageRep(Image::kImageRepSkia),
image_(image) {
@@ -224,6 +226,13 @@ Image::Image() {
// |storage_| is NULL for empty Images.
}
+Image::Image(const ImageSkia& image)
+ : storage_(new internal::ImageStorage(Image::kImageRepSkia)) {
+ internal::ImageRepSkia* rep = new internal::ImageRepSkia(
+ new ImageSkia(image));
+ AddRepresentation(rep);
+}
+
Image::Image(const SkBitmap* bitmap)
: storage_(new internal::ImageStorage(Image::kImageRepSkia)) {
internal::ImageRepSkia* rep = new internal::ImageRepSkia(
@@ -238,13 +247,6 @@ Image::Image(const SkBitmap& bitmap)
AddRepresentation(rep);
}
-Image::Image(const std::vector<const SkBitmap*>& bitmaps)
- : storage_(new internal::ImageStorage(Image::kImageRepSkia)) {
- internal::ImageRepSkia* rep = new internal::ImageRepSkia(
- new ImageSkia(bitmaps));
- AddRepresentation(rep);
-}
-
#if defined(TOOLKIT_GTK)
Image::Image(GdkPixbuf* pixbuf)
: storage_(new internal::ImageStorage(Image::kImageRepGdk)) {
@@ -274,7 +276,7 @@ Image::~Image() {
const SkBitmap* Image::ToSkBitmap() const {
internal::ImageRep* rep = GetRepresentation(Image::kImageRepSkia);
- return rep->AsImageRepSkia()->image()->bitmaps()[0];
+ return rep->AsImageRepSkia()->image()->bitmap();
}
const ImageSkia* Image::ToImageSkia() const {
@@ -301,6 +303,10 @@ NSImage* Image::ToNSImage() const {
}
#endif
+ImageSkia* Image::CopyImageSkia() const {
+ return new ImageSkia(*ToImageSkia());
+}
+
SkBitmap* Image::CopySkBitmap() const {
return new SkBitmap(*ToSkBitmap());
}
@@ -386,9 +392,8 @@ internal::ImageRep* Image::GetRepresentation(
#elif defined(OS_MACOSX)
if (storage_->default_representation_type() == Image::kImageRepCocoa) {
internal::ImageRepCocoa* nsimage_rep = default_rep->AsImageRepCocoa();
- std::vector<const SkBitmap*> bitmaps;
- CHECK(internal::NSImageToSkBitmaps(nsimage_rep->image(), &bitmaps));
- rep = new internal::ImageRepSkia(new ImageSkia(bitmaps));
+ ImageSkia image_skia = internal::NSImageToImageSkia(nsimage_rep->image());
+ rep = new internal::ImageRepSkia(new ImageSkia(image_skia));
}
#endif
CHECK(rep);
@@ -417,13 +422,13 @@ internal::ImageRep* Image::GetRepresentation(
#elif defined(TOOLKIT_GTK)
if (rep_type == Image::kImageRepGdk) {
GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(
- default_rep->AsImageRepSkia()->image()->bitmaps()[0]);
+ default_rep->AsImageRepSkia()->image()->bitmap());
native_rep = new internal::ImageRepGdk(pixbuf);
}
#elif defined(OS_MACOSX)
if (rep_type == Image::kImageRepCocoa) {
- NSImage* image = gfx::SkBitmapsToNSImage(
- default_rep->AsImageRepSkia()->image()->bitmaps());
+ NSImage* image = internal::ImageSkiaToNSImage(
+ default_rep->AsImageRepSkia()->image());
base::mac::NSObjectRetain(image);
native_rep = new internal::ImageRepCocoa(image);
}
diff --git a/ui/gfx/image/image.h b/ui/gfx/image/image.h
index 9c0e6eb..b5bb3bf 100644
--- a/ui/gfx/image/image.h
+++ b/ui/gfx/image/image.h
@@ -62,6 +62,9 @@ class UI_EXPORT Image {
// Creates an empty image with no representations.
Image();
+ // Creates a new image with the default representation.
+ explicit Image(const ImageSkia& image);
+
// Creates a new image with the default representation. The object will take
// ownership of the image.
explicit Image(const SkBitmap* bitmap);
@@ -70,10 +73,6 @@ class UI_EXPORT Image {
// representation.
explicit Image(const SkBitmap& bitmap);
- // To create an Image that supports multiple resolutions pass a vector
- // of bitmaps, one for each resolution.
- explicit Image(const std::vector<const SkBitmap*>& bitmaps);
-
#if defined(TOOLKIT_GTK)
// Does not increase |pixbuf|'s reference count; expects to take ownership.
explicit Image(GdkPixbuf* pixbuf);
@@ -112,6 +111,7 @@ class UI_EXPORT Image {
// backing pixels are shared amongst all copies (a fact of each of the
// converted representations, rather than a limitation imposed by Image) and
// so the result should be considered immutable.
+ ImageSkia* CopyImageSkia() const;
SkBitmap* CopySkBitmap() const;
#if defined(TOOLKIT_GTK)
GdkPixbuf* CopyGdkPixbuf() const;
diff --git a/ui/gfx/image/image_mac.mm b/ui/gfx/image/image_mac.mm
index 55b4aa4..eeed857 100644
--- a/ui/gfx/image/image_mac.mm
+++ b/ui/gfx/image/image_mac.mm
@@ -1,25 +1,46 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
#import <AppKit/AppKit.h>
+#import <vector>
+#include "base/memory/scoped_nsobject.h"
#include "base/memory/scoped_ptr.h"
#include "skia/ext/skia_utils_mac.h"
#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/image/image_skia.h"
namespace gfx {
namespace internal {
-bool NSImageToSkBitmaps(NSImage* image, std::vector<const SkBitmap*>* bitmaps) {
+gfx::ImageSkia NSImageToImageSkia(NSImage* image) {
+ gfx::ImageSkia image_skia;
for (NSImageRep* imageRep in [image representations]) {
- scoped_ptr<SkBitmap> bitmap(new SkBitmap(
- gfx::NSImageRepToSkBitmap(imageRep, [imageRep size], false)));
- if (bitmap->isNull())
- return false;
- bitmaps->push_back(bitmap.release());
+ NSSize imageRepSize = [imageRep size];
+ SkBitmap bitmap(gfx::NSImageRepToSkBitmap(imageRep, imageRepSize, false));
+ if (!bitmap.isNull() && !bitmap.empty()) {
+ float scaleFactor = imageRepSize.width / [image size].width;
+ image_skia.AddBitmapForScale(bitmap, scaleFactor);
+ }
}
- return true;
+ return image_skia;
+}
+
+NSImage* ImageSkiaToNSImage(const gfx::ImageSkia* image_skia) {
+ if (image_skia->empty())
+ return nil;
+
+ scoped_nsobject<NSImage> image([[NSImage alloc] init]);
+
+ const std::vector<SkBitmap> bitmaps = image_skia->bitmaps();
+ for (std::vector<SkBitmap>::const_iterator it = bitmaps.begin();
+ it != bitmaps.end(); ++it) {
+ [image addRepresentation:gfx::SkBitmapToNSBitmapImageRep(*it)];
+ }
+
+ [image setSize:NSMakeSize(image_skia->width(), image_skia->height())];
+ return [image.release() autorelease];
}
} // namespace internal
diff --git a/ui/gfx/image/image_mac_unittest.mm b/ui/gfx/image/image_mac_unittest.mm
index f0ed004..1de9c64 100644
--- a/ui/gfx/image/image_mac_unittest.mm
+++ b/ui/gfx/image/image_mac_unittest.mm
@@ -6,6 +6,7 @@
#include "base/logging.h"
#include "base/memory/scoped_nsobject.h"
+#include "base/memory/scoped_ptr.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/image/image.h"
@@ -31,57 +32,61 @@ class ImageMacTest : public testing::Test {
namespace gt = gfx::test;
-TEST_F(ImageMacTest, MultiResolutionNSImageToSkBitmap) {
- const int width1 = 10;
- const int height1 = 12;
- const int width2 = 20;
- const int height2 = 24;
+TEST_F(ImageMacTest, MultiResolutionNSImageToImageSkia) {
+ const int width1x = 10;
+ const int height1x = 12;
+ const int width2x = 20;
+ const int height2x = 24;
NSImageRep* image_rep_1;
- CreateBitmapImageRep(width1, height1, &image_rep_1);
+ CreateBitmapImageRep(width1x, height1x, &image_rep_1);
NSImageRep* image_rep_2;
- CreateBitmapImageRep(width2, height2, &image_rep_2);
+ CreateBitmapImageRep(width2x, height2x, &image_rep_2);
scoped_nsobject<NSImage> ns_image(
- [[NSImage alloc] initWithSize:NSMakeSize(width1, height1)]);
+ [[NSImage alloc] initWithSize:NSMakeSize(width1x, height1x)]);
[ns_image addRepresentation:image_rep_1];
[ns_image addRepresentation:image_rep_2];
gfx::Image image(ns_image.release());
EXPECT_EQ(1u, image.RepresentationCount());
- const std::vector<const SkBitmap*>& bitmaps = image.ToImageSkia()->bitmaps();
- EXPECT_EQ(2u, bitmaps.size());
-
- const SkBitmap* bitmap1 = bitmaps[0];
- EXPECT_TRUE(bitmap1);
- const SkBitmap* bitmap2 = bitmaps[1];
- EXPECT_TRUE(bitmap2);
-
- if (bitmap1->width() == width1) {
- EXPECT_EQ(bitmap1->height(), height1);
- EXPECT_EQ(bitmap2->width(), width2);
- EXPECT_EQ(bitmap2->height(), height2);
- } else {
- EXPECT_EQ(bitmap1->width(), width2);
- EXPECT_EQ(bitmap1->height(), height2);
- EXPECT_EQ(bitmap2->width(), width1);
- EXPECT_EQ(bitmap2->height(), height1);
- }
+
+ const gfx::ImageSkia* image_skia = image.ToImageSkia();
+ EXPECT_EQ(2u, image_skia->bitmaps().size());
+
+ float scale_factor;
+ const SkBitmap& bitmap1x = image_skia->GetBitmapForScale(1.0f, 1.0f,
+ &scale_factor);
+ EXPECT_TRUE(!bitmap1x.isNull());
+ EXPECT_EQ(1.0f, scale_factor);
+ EXPECT_EQ(width1x, bitmap1x.width());
+ EXPECT_EQ(height1x, bitmap1x.height());
+
+ const SkBitmap& bitmap2x = image_skia->GetBitmapForScale(2.0f, 2.0f,
+ &scale_factor);
+ EXPECT_TRUE(!bitmap2x.isNull());
+ EXPECT_EQ(2.0f, scale_factor);
+ EXPECT_EQ(width2x, bitmap2x.width());
+ EXPECT_EQ(height2x, bitmap2x.height());
// ToImageSkia should create a second representation.
EXPECT_EQ(2u, image.RepresentationCount());
}
-TEST_F(ImageMacTest, MultiResolutionSkBitmapToNSImage) {
- const int width1 = 10;
- const int height1 = 12;
- const int width2 = 20;
- const int height2 = 24;
+TEST_F(ImageMacTest, MultiResolutionImageSkiaToNSImage) {
+ const int width1x = 10;
+ const int height1x= 12;
+ const int width2x = 20;
+ const int height2x = 24;
+
+ scoped_ptr<SkBitmap> bitmap1x(gt::CreateBitmap(width1x, height1x));
+ scoped_ptr<SkBitmap> bitmap2x(gt::CreateBitmap(width2x, height2x));
- std::vector<const SkBitmap*> bitmaps;
- bitmaps.push_back(gt::CreateBitmap(width1, height1));
- bitmaps.push_back(gt::CreateBitmap(width2, height2));
- gfx::Image image(bitmaps);
+ gfx::ImageSkia image_skia;
+ image_skia.AddBitmapForScale(*bitmap1x, 1.0f);
+ image_skia.AddBitmapForScale(*bitmap2x, 2.0f);
+
+ gfx::Image image(image_skia);
EXPECT_EQ(1u, image.RepresentationCount());
EXPECT_EQ(2u, image.ToImageSkia()->bitmaps().size());
@@ -89,19 +94,24 @@ TEST_F(ImageMacTest, MultiResolutionSkBitmapToNSImage) {
NSImage* ns_image = image;
EXPECT_TRUE(ns_image);
+ // Image size should be the same as the 1x bitmap.
+ EXPECT_EQ([ns_image size].width, width1x);
+ EXPECT_EQ([ns_image size].height, height1x);
+
EXPECT_EQ(2u, [[image representations] count]);
NSImageRep* image_rep_1 = [[image representations] objectAtIndex:0];
NSImageRep* image_rep_2 = [[image representations] objectAtIndex:1];
- if ([image_rep_1 size].width == width1) {
- EXPECT_EQ([image_rep_1 size].height, height1);
- EXPECT_EQ([image_rep_2 size].width, width2);
- EXPECT_EQ([image_rep_2 size].height, height2);
+ if ([image_rep_1 size].width == width1x) {
+ EXPECT_EQ([image_rep_1 size].width, width1x);
+ EXPECT_EQ([image_rep_1 size].height, height1x);
+ EXPECT_EQ([image_rep_2 size].width, width2x);
+ EXPECT_EQ([image_rep_2 size].height, height2x);
} else {
- EXPECT_EQ([image_rep_1 size].width, width2);
- EXPECT_EQ([image_rep_1 size].height, height2);
- EXPECT_EQ([image_rep_2 size].width, width1);
- EXPECT_EQ([image_rep_2 size].height, height1);
+ EXPECT_EQ([image_rep_1 size].width, width2x);
+ EXPECT_EQ([image_rep_1 size].height, height2x);
+ EXPECT_EQ([image_rep_2 size].width, width1x);
+ EXPECT_EQ([image_rep_2 size].height, height1x);
}
// Cast to NSImage* should create a second representation.
diff --git a/ui/gfx/image/image_skia.cc b/ui/gfx/image/image_skia.cc
index 3c2e94f..d7ad469c 100644
--- a/ui/gfx/image/image_skia.cc
+++ b/ui/gfx/image/image_skia.cc
@@ -8,139 +8,200 @@
#include <cmath>
#include "base/logging.h"
-#include "base/stl_util.h"
+#include "ui/gfx/size.h"
+#include "base/message_loop.h"
+#include "third_party/skia/include/core/SkPixelRef.h"
namespace gfx {
-ImageSkia::ImageSkia(const SkBitmap* bitmap)
- : size_(bitmap->width(), bitmap->height()),
- mip_map_build_pending_(false) {
- CHECK(bitmap);
- // TODO(pkotwicz): Add a CHECK to ensure that !bitmap->isNull()
- bitmaps_.push_back(bitmap);
-}
-
-ImageSkia::ImageSkia(const std::vector<const SkBitmap*>& bitmaps)
- : bitmaps_(bitmaps),
- mip_map_build_pending_(false) {
- CHECK(!bitmaps_.empty());
- // TODO(pkotwicz): Add a CHECK to ensure that !bitmap->isNull() for each
- // vector element.
- // Assume that the smallest bitmap represents 1x scale factor.
- for (size_t i = 0; i < bitmaps_.size(); ++i) {
- gfx::Size bitmap_size(bitmaps_[i]->width(), bitmaps_[i]->height());
- if (size_.IsEmpty() || bitmap_size.GetArea() < size_.GetArea())
- size_ = bitmap_size;
+namespace internal {
+
+// A helper class such that ImageSkia can be cheaply copied. ImageSkia holds a
+// refptr instance of ImageSkiaStorage, which in turn holds all of ImageSkia's
+// information.
+class ImageSkiaStorage : public base::RefCounted<ImageSkiaStorage> {
+ public:
+ ImageSkiaStorage() {
}
-}
-ImageSkia::~ImageSkia() {
- STLDeleteElements(&bitmaps_);
-}
+ void AddBitmap(const SkBitmap& bitmap) {
+ bitmaps_.push_back(bitmap);
+ }
-void ImageSkia::BuildMipMap() {
- mip_map_build_pending_ = true;
-}
+ const std::vector<SkBitmap>& bitmaps() const { return bitmaps_; }
-void ImageSkia::DrawToCanvasInt(gfx::Canvas* canvas, int x, int y) {
- SkPaint p;
- DrawToCanvasInt(canvas, x, y, p);
-}
+ void set_size(const gfx::Size& size) { size_ = size; }
+ const gfx::Size& size() const { return size_; }
-void ImageSkia::DrawToCanvasInt(gfx::Canvas* canvas,
- int x, int y,
- const SkPaint& paint) {
+ private:
+ ~ImageSkiaStorage() {
+ }
- if (IsZeroSized())
- return;
+ // Bitmaps at different densities.
+ std::vector<SkBitmap> bitmaps_;
- SkMatrix m = canvas->sk_canvas()->getTotalMatrix();
- float scale_x = std::abs(SkScalarToFloat(m.getScaleX()));
- float scale_y = std::abs(SkScalarToFloat(m.getScaleY()));
+ // Size of the image in DIP.
+ gfx::Size size_;
- const SkBitmap* bitmap = GetBitmapForScale(scale_x, scale_y);
+ friend class base::RefCounted<ImageSkiaStorage>;
+};
- if (mip_map_build_pending_) {
- const_cast<SkBitmap*>(bitmap)->buildMipMap();
- mip_map_build_pending_ = false;
- }
+} // internal
- float bitmap_scale_x = static_cast<float>(bitmap->width()) / width();
- float bitmap_scale_y = static_cast<float>(bitmap->height()) / height();
+ImageSkia::ImageSkia() : storage_(NULL) {
+}
- canvas->Save();
- canvas->sk_canvas()->scale(1.0f / bitmap_scale_x,
- 1.0f / bitmap_scale_y);
- canvas->sk_canvas()->drawBitmap(*bitmap, SkFloatToScalar(x * bitmap_scale_x),
- SkFloatToScalar(y * bitmap_scale_y));
- canvas->Restore();
+ImageSkia::ImageSkia(const SkBitmap& bitmap) {
+ Init(bitmap);
}
-void ImageSkia::DrawToCanvasInt(gfx::Canvas* canvas,
- int src_x, int src_y, int src_w, int src_h,
- int dest_x, int dest_y, int dest_w, int dest_h,
- bool filter) {
- SkPaint p;
- DrawToCanvasInt(canvas, src_x, src_y, src_w, src_h, dest_x, dest_y,
- dest_w, dest_h, filter, p);
+ImageSkia::ImageSkia(const SkBitmap& bitmap, float dip_scale_factor) {
+ Init(bitmap, dip_scale_factor);
}
-void ImageSkia::DrawToCanvasInt(gfx::Canvas* canvas,
- int src_x, int src_y, int src_w, int src_h,
- int dest_x, int dest_y, int dest_w, int dest_h,
- bool filter,
- const SkPaint& paint) {
- if (IsZeroSized())
- return;
+ImageSkia::ImageSkia(const SkBitmap* bitmap) {
+ Init(*bitmap);
- SkMatrix m = canvas->sk_canvas()->getTotalMatrix();
- float scale_x = std::abs(SkScalarToFloat(m.getScaleX()));
- float scale_y = std::abs(SkScalarToFloat(m.getScaleY()));
+ if (MessageLoop::current()) {
+ // Use DeleteSoon such that |bitmap| is still valid if caller uses |bitmap|
+ // immediately after having called constructor.
+ MessageLoop::current()->DeleteSoon(FROM_HERE, bitmap);
+ } else {
+ // Hit in unittests.
+ delete bitmap;
+ }
+}
+
+ImageSkia::ImageSkia(const ImageSkia& other) : storage_(other.storage_) {
+}
+
+ImageSkia& ImageSkia::operator=(const ImageSkia& other) {
+ storage_ = other.storage_;
+ return *this;
+}
- const SkBitmap* bitmap = GetBitmapForScale(scale_x, scale_y);
+ImageSkia& ImageSkia::operator=(const SkBitmap& other) {
+ Init(other);
+ return *this;
+}
- if (mip_map_build_pending_) {
- const_cast<SkBitmap*>(bitmap)->buildMipMap();
- mip_map_build_pending_ = false;
+ImageSkia::operator SkBitmap&() const {
+ if (isNull()) {
+ // TODO(pkotwicz): This is temporary till conversion to gfx::ImageSkia is
+ // done.
+ // Static null bitmap such that we are not returning a temporary.
+ static SkBitmap* null_bitmap = new SkBitmap();
+ return *null_bitmap;
}
- float bitmap_scale_x = static_cast<float>(bitmap->width()) / width();
- float bitmap_scale_y = static_cast<float>(bitmap->height()) / height();
+ return const_cast<SkBitmap&>(storage_->bitmaps()[0]);
+}
- canvas->Save();
- canvas->sk_canvas()->scale(1.0f / bitmap_scale_x,
- 1.0f / bitmap_scale_y);
- canvas->DrawBitmapFloat(*bitmap,
- src_x * bitmap_scale_x, src_y * bitmap_scale_x,
- src_w * bitmap_scale_x, src_h * bitmap_scale_y,
- dest_x * bitmap_scale_x, dest_y * bitmap_scale_y,
- dest_w * bitmap_scale_x, dest_h * bitmap_scale_y,
- filter, paint);
+ImageSkia::~ImageSkia() {
+}
- canvas->Restore();
+void ImageSkia::AddBitmapForScale(const SkBitmap& bitmap,
+ float dip_scale_factor) {
+ DCHECK(!bitmap.isNull());
+
+ if (isNull()) {
+ Init(bitmap, dip_scale_factor);
+ } else {
+ // We currently assume that the bitmap size = 1x size * |dip_scale_factor|.
+ // TODO(pkotwicz): Do something better because of rounding errors when
+ // |dip_scale_factor| is not an int.
+ // TODO(pkotwicz): Remove DCHECK for dip_scale_factor == 1.0f once
+ // image_loading_tracker correctly handles multiple sized images.
+ DCHECK(dip_scale_factor == 1.0f ||
+ static_cast<int>(width() * dip_scale_factor) == bitmap.width());
+ DCHECK(dip_scale_factor == 1.0f ||
+ static_cast<int>(height() * dip_scale_factor) == bitmap.height());
+ storage_->AddBitmap(bitmap);
+ }
}
-const SkBitmap* ImageSkia::GetBitmapForScale(float x_scale_factor,
- float y_scale_factor) const {
+const SkBitmap& ImageSkia::GetBitmapForScale(float x_scale_factor,
+ float y_scale_factor,
+ float* bitmap_scale_factor) const {
+
+ // Static null bitmap such that we are not returning a temporary.
+ static SkBitmap* null_bitmap = new SkBitmap();
+
+ if (empty())
+ return *null_bitmap;
+
// Get the desired bitmap width and height given |x_scale_factor|,
- // |y_scale_factor| and |size_| at 1x density.
- float desired_width = size_.width() * x_scale_factor;
- float desired_height = size_.height() * y_scale_factor;
+ // |y_scale_factor| and size at 1x density.
+ float desired_width = width() * x_scale_factor;
+ float desired_height = height() * y_scale_factor;
+ const std::vector<SkBitmap>& bitmaps = storage_->bitmaps();
size_t closest_index = 0;
float smallest_diff = std::numeric_limits<float>::max();
- for (size_t i = 0; i < bitmaps_.size(); ++i) {
- if (bitmaps_[i]->isNull())
- continue;
-
- float diff = std::abs(bitmaps_[i]->width() - desired_width) +
- std::abs(bitmaps_[i]->height() - desired_height);
+ for (size_t i = 0; i < bitmaps.size(); ++i) {
+ float diff = std::abs(bitmaps[i].width() - desired_width) +
+ std::abs(bitmaps[i].height() - desired_height);
if (diff < smallest_diff) {
closest_index = i;
smallest_diff = diff;
}
}
- return bitmaps_[closest_index];
+ if (smallest_diff < std::numeric_limits<float>::max()) {
+ *bitmap_scale_factor = bitmaps[closest_index].width() / width();
+ return bitmaps[closest_index];
+ }
+
+ return *null_bitmap;
+}
+
+bool ImageSkia::empty() const {
+ return isNull() || storage_->size().IsEmpty();
+}
+
+int ImageSkia::width() const {
+ return isNull() ? 0 : storage_->size().width();
+}
+
+int ImageSkia::height() const {
+ return isNull() ? 0 : storage_->size().height();
+}
+
+bool ImageSkia::extractSubset(ImageSkia* dst, SkIRect& subset) const {
+ if (isNull())
+ return false;
+ SkBitmap dst_bitmap;
+ bool return_value = storage_->bitmaps()[0].extractSubset(&dst_bitmap,
+ subset);
+ *dst = ImageSkia(dst_bitmap);
+ return return_value;
+}
+
+const std::vector<SkBitmap> ImageSkia::bitmaps() const {
+ return storage_->bitmaps();
+}
+
+const SkBitmap* ImageSkia::bitmap() const {
+ if (isNull())
+ return NULL;
+
+ return &storage_->bitmaps()[0];
+}
+
+void ImageSkia::Init(const SkBitmap& bitmap) {
+ Init(bitmap, 1.0f);
+}
+
+void ImageSkia::Init(const SkBitmap& bitmap, float scale_factor) {
+ DCHECK_GT(scale_factor, 0.0f);
+ // TODO(pkotwicz): The image should be null whenever bitmap is null.
+ if (bitmap.empty()) {
+ storage_ = NULL;
+ return;
+ }
+ storage_ = new internal::ImageSkiaStorage();
+ storage_->set_size(gfx::Size(static_cast<int>(bitmap.width() / scale_factor),
+ static_cast<int>(bitmap.height() / scale_factor)));
+ storage_->AddBitmap(bitmap);
}
} // namespace gfx
diff --git a/ui/gfx/image/image_skia.h b/ui/gfx/image/image_skia.h
index bdfb53f..8b911cb 100644
--- a/ui/gfx/image/image_skia.h
+++ b/ui/gfx/image/image_skia.h
@@ -9,80 +9,119 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/size.h"
+#include "ui/base/ui_export.h"
namespace gfx {
-// Container for the same image at different densities, similar to NSImage.
-// Smallest image is assumed to represent 1x density.
+namespace internal {
+class ImageSkiaStorage;
+} // namespace internal
-// ImageSkia should be used for caching and drawing images of different
-// densities. It should not be used as an SkBitmap wrapper.
+// Container for the same image at different densities, similar to NSImage.
+// Image height and width are in DIP (Device Indepent Pixel) coordinates.
+//
+// ImageSkia should be used whenever possible instead of SkBitmap.
+// Functions that mutate the image should operate on the SkBitmap returned
+// from ImageSkia::GetBitmapForScale, not on ImageSkia.
+//
+// ImageSkia is cheap to copy and intentionally supports copy semantics.
class UI_EXPORT ImageSkia {
public:
+ // Creates instance with no bitmaps.
+ ImageSkia();
+
+ // Adds ref to passed in bitmap.
+ // DIP width and height are set based on scale factor of 1x.
+ // TODO(pkotwicz): This is temporary till conversion to gfx::ImageSkia is
+ // done.
+ ImageSkia(const SkBitmap& bitmap);
+
+ // Adds ref to passed in bitmap.
+ // DIP width and height are set based on |dip_scale_factor|.
+ ImageSkia(const SkBitmap& bitmap, float dip_scale_factor);
+
+ // Takes ownership of passed in bitmap.
+ // Caller should not assume that |bitmap| will be valid after constructor
+ // is called.
+ // DIP width and height are set based on scale factor of 1x.
+ // TODO(pkotwicz): This is temporary till conversion to gfx::ImageSkia is
+ // done.
explicit ImageSkia(const SkBitmap* bitmap);
- explicit ImageSkia(const std::vector<const SkBitmap*>& bitmaps);
- ~ImageSkia();
- // Build mipmap at time of next call to |DrawToCanvasInt|.
- void BuildMipMap();
-
- // Draws the image with the origin at the specified location. The upper left
- // corner of the image is rendered at the specified location.
- void DrawToCanvasInt(Canvas* canvas, int x, int y);
-
- // Draws the image with the origin at the specified location, using the
- // specified paint. The upper left corner of the image is rendered at the
- // specified location.
- void DrawToCanvasInt(Canvas* canvas,
- int x, int y,
- const SkPaint& paint);
-
- // Draws a portion of the image in the specified location. The src parameters
- // correspond to the region of the image to draw in the region defined
- // by the dest coordinates.
- //
- // If the width or height of the source differs from that of the destination,
- // the image will be scaled. When scaling down, it is highly recommended
- // that you call BuildMipMap() on your image to ensure that it has
- // a mipmap, which will result in much higher-quality output. Set |filter| to
- // use filtering for bitmaps, otherwise the nearest-neighbor algorithm is used
- // for resampling.
- //
- // An optional custom SkPaint can be provided.
- void DrawToCanvasInt(Canvas* canvas,
- int src_x, int src_y, int src_w, int src_h,
- int dest_x, int dest_y, int dest_w, int dest_h,
- bool filter);
- void DrawToCanvasInt(Canvas* canvas,
- int src_x, int src_y, int src_w, int src_h,
- int dest_x, int dest_y, int dest_w, int dest_h,
- bool filter,
- const SkPaint& paint);
-
- // Returns true if |size_| is empty.
- bool IsZeroSized() const { return size_.IsEmpty(); }
+ // Copies a reference to |other|'s storage.
+ ImageSkia(const ImageSkia& other);
- // Width and height of image in DIP coordinate system.
- int width() const { return size_.width(); }
- int height() const { return size_.height(); }
+ // Copies a reference to |other|'s storage.
+ ImageSkia& operator=(const ImageSkia& other);
- // Returns a vector with the SkBitmaps contained in this object.
- const std::vector<const SkBitmap*>& bitmaps() const { return bitmaps_; }
+ // Converts from SkBitmap.
+ // Adds ref to passed in bitmap.
+ // DIP width and height are set based on scale factor of 1x.
+ // TODO(pkotwicz): This is temporary till conversion to gfx::ImageSkia is
+ // done.
+ ImageSkia& operator=(const SkBitmap& other);
+
+ // Converts to SkBitmap.
+ // TODO(pkotwicz): This is temporary till conversion to gfx::ImageSkia is
+ // done.
+ operator SkBitmap&() const;
+
+ ~ImageSkia();
+
+ // Adds |bitmap| for |dip_scale_factor| to bitmaps contained by this object.
+ // Adds ref to passed in bitmap.
+ // DIP width and height are set based on |dip_scale_factor|.
+ void AddBitmapForScale(const SkBitmap& bitmap, float dip_scale_factor);
- private:
// Returns the bitmap whose density best matches |x_scale_factor| and
// |y_scale_factor|.
- const SkBitmap* GetBitmapForScale(float x_scale_factor,
- float y_scale_factor) const;
+ // Returns a null bitmap if object contains no bitmaps.
+ // |bitmap_scale_factor| is set to the scale factor of the returned bitmap.
+ const SkBitmap& GetBitmapForScale(float x_scale_factor,
+ float y_scale_factor,
+ float* bitmap_scale_factor) const;
+
+ // Returns true if object is null or |size_| is empty.
+ bool empty() const;
+
+ // Returns true if this is a null object.
+ // TODO(pkotwicz): Merge this function into empty().
+ bool isNull() const { return storage_ == NULL; }
+
+ // Width and height of image in DIP coordinate system.
+ int width() const;
+ int height() const;
- std::vector<const SkBitmap*> bitmaps_;
- gfx::Size size_;
- bool mip_map_build_pending_;
+ // Wrapper function for SkBitmap::extractBitmap.
+ // Operates on bitmap at index 0 if available.
+ // TODO(pkotwicz): This is temporary till conversion to gfx::ImageSkia is
+ // done.
+ bool extractSubset(ImageSkia* dst, SkIRect& subset) const;
- DISALLOW_COPY_AND_ASSIGN(ImageSkia);
+ // Returns pointer to an SkBitmap contained by this object.
+ // TODO(pkotwicz): This is temporary till conversion to gfx::ImageSkia is
+ // done.
+ const SkBitmap* bitmap() const;
+
+ // Returns a vector with the SkBitmaps contained in this object.
+ const std::vector<SkBitmap> bitmaps() const;
+
+ private:
+ // Initialize ImageStorage with passed in parameters.
+ // If |bitmap.isNull()|, ImageStorage is set to NULL.
+ // Scale factor is set based on default scale factor of 1x.
+ // TODO(pkotwicz): This is temporary till conversion to gfx::ImageSkia is
+ // done.
+ void Init(const SkBitmap& bitmap);
+
+ // Initialize ImageStorage with passed in parameters.
+ // If |bitmap.isNull()|, ImageStorage is set to NULL.
+ void Init(const SkBitmap& bitmap, float scale_factor);
+
+ // A refptr so that ImageRepSkia can be copied cheaply.
+ scoped_refptr<internal::ImageSkiaStorage> storage_;
};
} // namespace gfx
diff --git a/ui/gfx/image/image_unittest.cc b/ui/gfx/image/image_unittest.cc
index 8793562..7b50be8 100644
--- a/ui/gfx/image/image_unittest.cc
+++ b/ui/gfx/image/image_unittest.cc
@@ -251,41 +251,39 @@ TEST_F(ImageTest, Assign) {
EXPECT_EQ(image1.ToSkBitmap(), image2.ToSkBitmap());
}
-TEST_F(ImageTest, MultiResolutionSkBitmap) {
- const int width1 = 10;
- const int height1 = 12;
- const int width2 = 20;
- const int height2 = 24;
-
- std::vector<const SkBitmap*> bitmaps;
- bitmaps.push_back(gt::CreateBitmap(width1, height1));
- bitmaps.push_back(gt::CreateBitmap(width2, height2));
- gfx::Image image(bitmaps);
-
- EXPECT_EQ(1u, image.RepresentationCount());
- const std::vector<const SkBitmap*>& image_bitmaps =
- image.ToImageSkia()->bitmaps();
- EXPECT_EQ(2u, image_bitmaps.size());
-
- const SkBitmap* bitmap1 = image_bitmaps[0];
- EXPECT_TRUE(bitmap1);
- const SkBitmap* bitmap2 = image_bitmaps[1];
- EXPECT_TRUE(bitmap2);
-
- if (bitmap1->width() == width1) {
- EXPECT_EQ(bitmap1->height(), height1);
- EXPECT_EQ(bitmap2->width(), width2);
- EXPECT_EQ(bitmap2->height(), height2);
- } else {
- EXPECT_EQ(bitmap1->width(), width2);
- EXPECT_EQ(bitmap1->height(), height2);
- EXPECT_EQ(bitmap2->width(), width1);
- EXPECT_EQ(bitmap2->height(), height1);
- }
-
- // Sanity check.
+TEST_F(ImageTest, MultiResolutionImage) {
+ const int width1x = 10;
+ const int height1x = 12;
+ const int width2x = 20;
+ const int height2x = 24;
+
+ scoped_ptr<SkBitmap> bitmap1(gt::CreateBitmap(width1x, height1x));
+ scoped_ptr<SkBitmap> bitmap2(gt::CreateBitmap(width2x, height2x));
+
+ gfx::ImageSkia image_skia;
+ image_skia.AddBitmapForScale(*bitmap1, 1.0f);
+ image_skia.AddBitmapForScale(*bitmap2, 2.0f);
+
+ EXPECT_EQ(2u, image_skia.bitmaps().size());
+
+ float scale_factor;
+ const SkBitmap& bitmap1x = image_skia.GetBitmapForScale(1.0f, 1.0f,
+ &scale_factor);
+ EXPECT_TRUE(!bitmap1x.isNull());
+ EXPECT_EQ(1.0f, scale_factor);
+ EXPECT_EQ(width1x, bitmap1x.width());
+ EXPECT_EQ(height1x, bitmap1x.height());
+
+ const SkBitmap& bitmap2x = image_skia.GetBitmapForScale(2.0f, 2.0f,
+ &scale_factor);
+ EXPECT_TRUE(!bitmap2x.isNull());
+ EXPECT_EQ(2.0f, scale_factor);
+ EXPECT_EQ(width2x, bitmap2x.width());
+ EXPECT_EQ(height2x, bitmap2x.height());
+
+ // Check that the image has a single representation.
+ gfx::Image image(image_skia);
EXPECT_EQ(1u, image.RepresentationCount());
- EXPECT_EQ(2u, image.ToImageSkia()->bitmaps().size());
}
// Integration tests with UI toolkit frameworks require linking against the