diff options
author | pkotwicz@chromium.org <pkotwicz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-10 17:41:13 +0000 |
---|---|---|
committer | pkotwicz@chromium.org <pkotwicz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-10 17:41:13 +0000 |
commit | 89753fdf570d25d44fe9d5f01600fa5138ca1342 (patch) | |
tree | be9814b040e47a28f25578073baaf3aacf673f78 | |
parent | 8bd4a46da03767be3a2fa16c9fc32ed2233e47f7 (diff) | |
download | chromium_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.cc | 9 | ||||
-rw-r--r-- | chrome/browser/extensions/image_loading_tracker_unittest.cc | 7 | ||||
-rw-r--r-- | chrome/browser/themes/browser_theme_pack.cc | 41 | ||||
-rw-r--r-- | chrome/browser/web_applications/web_app_mac.mm | 5 | ||||
-rw-r--r-- | skia/ext/skia_utils_mac.h | 10 | ||||
-rw-r--r-- | skia/ext/skia_utils_mac.mm | 46 | ||||
-rw-r--r-- | skia/ext/skia_utils_mac_unittest.mm | 40 | ||||
-rw-r--r-- | ui/base/resource/resource_bundle.cc | 22 | ||||
-rw-r--r-- | ui/gfx/canvas.cc | 107 | ||||
-rw-r--r-- | ui/gfx/canvas.h | 56 | ||||
-rw-r--r-- | ui/gfx/image/image.cc | 35 | ||||
-rw-r--r-- | ui/gfx/image/image.h | 8 | ||||
-rw-r--r-- | ui/gfx/image/image_mac.mm | 37 | ||||
-rw-r--r-- | ui/gfx/image/image_mac_unittest.mm | 96 | ||||
-rw-r--r-- | ui/gfx/image/image_skia.cc | 255 | ||||
-rw-r--r-- | ui/gfx/image/image_skia.h | 155 | ||||
-rw-r--r-- | ui/gfx/image/image_unittest.cc | 66 |
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 |