diff options
author | sail@chromium.org <sail@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-27 20:50:52 +0000 |
---|---|---|
committer | sail@chromium.org <sail@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-27 20:50:52 +0000 |
commit | 7b9cf72a104f1237de9d4ee29b81e03516106084 (patch) | |
tree | 1af51fc4d266a2b21c760947d37c8d3498ca2397 /skia | |
parent | e6c61281ca34ea34e8408bd4fd01daf4c8e4c5fd (diff) | |
download | chromium_src-7b9cf72a104f1237de9d4ee29b81e03516106084.zip chromium_src-7b9cf72a104f1237de9d4ee29b81e03516106084.tar.gz chromium_src-7b9cf72a104f1237de9d4ee29b81e03516106084.tar.bz2 |
Use large icon resource pak
This is part of change r82185 that was reverted due to a performance regression on 10.5.
The performance regression was due to a change in skia_utils_mac.mm to draw images using -[NSImageRep drawRect:] instead of -[NSImage drawRect:...].
It turns out that on 10.5 -[NSImage drawRect:...] does caching that imporoves performance for subsequent drawing by as much as 4x.
This change is similar to r82185 expect that we now use -[NSImage drawRect:] if available.
BUG=75812
TEST=Ran unit tests locally, try jobs pending.
Review URL: http://codereview.chromium.org/6905003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@83206 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'skia')
-rw-r--r-- | skia/ext/skia_utils_mac.h | 10 | ||||
-rw-r--r-- | skia/ext/skia_utils_mac.mm | 199 | ||||
-rw-r--r-- | skia/ext/skia_utils_mac_unittest.mm | 136 |
3 files changed, 270 insertions, 75 deletions
diff --git a/skia/ext/skia_utils_mac.h b/skia/ext/skia_utils_mac.h index 0551643..04d4a1d 100644 --- a/skia/ext/skia_utils_mac.h +++ b/skia/ext/skia_utils_mac.h @@ -7,6 +7,7 @@ #pragma once #include <CoreGraphics/CGColor.h> +#include <vector> #include "third_party/skia/include/core/SkColor.h" @@ -23,8 +24,10 @@ typedef struct _NSSize NSSize; #ifdef __OBJC__ @class NSImage; +@class NSImageRep; #else class NSImage; +class NSImageRep; #endif namespace gfx { @@ -64,6 +67,9 @@ SkBitmap CGImageToSkBitmap(CGImageRef image); // Draws an NSImage with a given size into a SkBitmap. SkBitmap NSImageToSkBitmap(NSImage* image, NSSize size, bool is_opaque); +// Draws an NSImageRep with a given size into a SkBitmap. +SkBitmap NSImageRepToSkBitmap(NSImageRep* image, NSSize size, bool is_opaque); + // Given an SkBitmap and a color space, return an autoreleased NSImage. NSImage* SkBitmapToNSImageWithColorSpace(const SkBitmap& icon, CGColorSpaceRef colorSpace); @@ -73,6 +79,10 @@ NSImage* SkBitmapToNSImageWithColorSpace(const SkBitmap& icon, // TODO(thakis): Remove this -- http://crbug.com/69432 NSImage* SkBitmapToNSImage(const SkBitmap& icon); +// Given a vector of SkBitmaps, return an NSImage with each bitmap added +// as a representation. +NSImage* SkBitmapsToNSImage(const std::vector<const SkBitmap*>& bitmaps); + // Returns |[NSImage imageNamed:@"NSApplicationIcon"]| as SkBitmap. SkBitmap AppplicationIconAtSize(int size); diff --git a/skia/ext/skia_utils_mac.mm b/skia/ext/skia_utils_mac.mm index c78121a..5547c4a 100644 --- a/skia/ext/skia_utils_mac.mm +++ b/skia/ext/skia_utils_mac.mm @@ -8,9 +8,110 @@ #include "base/logging.h" #include "base/mac/scoped_cftyperef.h" +#include "base/memory/scoped_nsobject.h" #include "base/memory/scoped_ptr.h" #include "skia/ext/bitmap_platform_device_mac.h" #include "third_party/skia/include/utils/mac/SkCGUtils.h" +#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" + +// 10.6 API that we use if available. +#if !defined(MAC_OS_X_VERSION_10_6) || \ + MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 + +@interface NSImageRep (NSObject) + +- (BOOL)drawInRect:(NSRect)dstSpacePortionRect + fromRect:(NSRect)srcSpacePortionRect + operation:(NSCompositingOperation)op + fraction:(CGFloat)requestedAlpha + respectFlipped:(BOOL)respectContextIsFlipped + hints:(NSDictionary*)hints; + +@end + +#endif // OSes < Mac OS X 10.6 + +namespace { + +// Draws an NSImage or an NSImageRep with a given size into a SkBitmap. +SkBitmap NSImageOrNSImageRepToSkBitmap( + NSImage* image, + NSImageRep* image_rep, + NSSize size, + bool is_opaque) { + // Only image or image_rep should be provided, not both. + DCHECK((image != 0) ^ (image_rep != 0)); + + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width, size.height); + if (!bitmap.allocPixels()) + return bitmap; // Return |bitmap| which should respond true to isNull(). + + bitmap.setIsOpaque(is_opaque); + + base::mac::ScopedCFTypeRef<CGColorSpaceRef> color_space( + CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)); + void* data = bitmap.getPixels(); + + // Allocate a bitmap context with 4 components per pixel (BGRA). Apple + // recommends these flags for improved CG performance. +#define HAS_ARGB_SHIFTS(a, r, g, b) \ + (SK_A32_SHIFT == (a) && SK_R32_SHIFT == (r) \ + && SK_G32_SHIFT == (g) && SK_B32_SHIFT == (b)) +#if defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0) + base::mac::ScopedCFTypeRef<CGContextRef> context( + CGBitmapContextCreate(data, size.width, size.height, 8, size.width*4, + color_space, + kCGImageAlphaPremultipliedFirst | + kCGBitmapByteOrder32Host)); +#else +#error We require that Skia's and CoreGraphics's recommended \ + image memory layout match. +#endif +#undef HAS_ARGB_SHIFTS + + // Something went really wrong. Best guess is that the bitmap data is invalid. + DCHECK(context); + + // Save the current graphics context so that we can restore it later. + gfx::ScopedNSGraphicsContextSaveGState scoped_g_state; + + // Dummy context that we will draw into. + NSGraphicsContext* context_cocoa = + [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO]; + [NSGraphicsContext setCurrentContext:context_cocoa]; + + // This will stretch any images to |size| if it does not fit or is non-square. + NSRect drawRect = NSMakeRect(0, 0, size.width, size.height); + + // NSImage does caching such that subsequent drawing is much faster (on my + // machine, about 4x faster). Unfortunately on 10.5 NSImageRep doesn't do + // caching. For this reason we draw using an NSImage if available. Once + // 10.5 is no longer supported we can drop this and always use NSImageRep. + if (image) { + [image drawInRect:drawRect + fromRect:NSZeroRect + operation:NSCompositeCopy + fraction:1.0]; + } else { + // Use NSCompositeCopy if available, it's slightly faster. + if ([image_rep respondsToSelector:@selector( + drawInRect:fromRect:operation:fraction:respectFlipped:hints:)]) { + [image_rep drawInRect:drawRect + fromRect:NSZeroRect + operation:NSCompositeCopy + fraction:1.0 + respectFlipped:NO + hints:NO]; + } else { + [image_rep drawInRect:drawRect]; + } + } + + return bitmap; +} + +} // namespace namespace gfx { @@ -68,7 +169,7 @@ CGRect SkRectToCGRect(const SkRect& rect) { // Converts CGColorRef to the ARGB layout Skia expects. SkColor CGColorRefToSkColor(CGColorRef color) { DCHECK(CGColorGetNumberOfComponents(color) == 4); - const CGFloat *components = CGColorGetComponents(color); + const CGFloat* components = CGColorGetComponents(color); return SkColorSetARGB(SkScalarRound(255.0 * components[3]), // alpha SkScalarRound(255.0 * components[0]), // red SkScalarRound(255.0 * components[1]), // green @@ -116,55 +217,11 @@ SkBitmap CGImageToSkBitmap(CGImageRef image) { } SkBitmap NSImageToSkBitmap(NSImage* image, NSSize size, bool is_opaque) { - SkBitmap bitmap; - bitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width, size.height); - if (bitmap.allocPixels() != true) - return bitmap; // Return |bitmap| which should respond true to isNull(). - - bitmap.setIsOpaque(is_opaque); - - base::mac::ScopedCFTypeRef<CGColorSpaceRef> color_space( - CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)); - void* data = bitmap.getPixels(); - - // Allocate a bitmap context with 4 components per pixel (BGRA). Apple - // recommends these flags for improved CG performance. -#define HAS_ARGB_SHIFTS(a, r, g, b) \ - (SK_A32_SHIFT == (a) && SK_R32_SHIFT == (r) \ - && SK_G32_SHIFT == (g) && SK_B32_SHIFT == (b)) -#if defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0) - base::mac::ScopedCFTypeRef<CGContextRef> context( - CGBitmapContextCreate(data, size.width, size.height, 8, size.width*4, - color_space, - kCGImageAlphaPremultipliedFirst | - kCGBitmapByteOrder32Host)); -#else -#error We require that Skia's and CoreGraphics's recommended \ - image memory layout match. -#endif -#undef HAS_ARGB_SHIFTS - - // Something went really wrong. Best guess is that the bitmap data is invalid. - DCHECK(context != NULL); - - // Save the current graphics context so that we can restore it later. - [NSGraphicsContext saveGraphicsState]; - - // Dummy context that we will draw into. - NSGraphicsContext* context_cocoa = - [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO]; - [NSGraphicsContext setCurrentContext:context_cocoa]; - - // This will stretch any images to |size| if it does not fit or is non-square. - [image drawInRect:NSMakeRect(0, 0, size.width, size.height) - fromRect:NSZeroRect - operation:NSCompositeCopy - fraction:1.0]; - - // Done drawing, restore context. - [NSGraphicsContext restoreGraphicsState]; + return NSImageOrNSImageRepToSkBitmap(image, nil, size, is_opaque); +} - return bitmap; +SkBitmap NSImageRepToSkBitmap(NSImageRep* image, NSSize size, bool is_opaque) { + return NSImageOrNSImageRepToSkBitmap(nil, image, size, is_opaque); } NSImage* SkBitmapToNSImageWithColorSpace(const SkBitmap& skiaBitmap, @@ -173,17 +230,16 @@ NSImage* SkBitmapToNSImageWithColorSpace(const SkBitmap& skiaBitmap, return nil; // First convert SkBitmap to CGImageRef. - CGImageRef cgimage = - SkCreateCGImageRefWithColorspace(skiaBitmap, colorSpace); + base::mac::ScopedCFTypeRef<CGImageRef> cgimage( + SkCreateCGImageRefWithColorspace(skiaBitmap, colorSpace)); // Now convert to NSImage. - NSBitmapImageRep* bitmap = [[[NSBitmapImageRep alloc] - initWithCGImage:cgimage] autorelease]; - CFRelease(cgimage); - NSImage* image = [[[NSImage alloc] init] autorelease]; + scoped_nsobject<NSBitmapImageRep> bitmap( + [[NSBitmapImageRep alloc] initWithCGImage:cgimage]); + scoped_nsobject<NSImage> image([[NSImage alloc] init]); [image addRepresentation:bitmap]; [image setSize:NSMakeSize(skiaBitmap.width(), skiaBitmap.height())]; - return image; + return [image.release() autorelease]; } NSImage* SkBitmapToNSImage(const SkBitmap& skiaBitmap) { @@ -192,6 +248,37 @@ 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); diff --git a/skia/ext/skia_utils_mac_unittest.mm b/skia/ext/skia_utils_mac_unittest.mm index 7401f11..48fa407 100644 --- a/skia/ext/skia_utils_mac_unittest.mm +++ b/skia/ext/skia_utils_mac_unittest.mm @@ -9,39 +9,60 @@ namespace { class SkiaUtilsMacTest : public testing::Test { public: + // Creates a red or blue bitmap. + SkBitmap CreateSkBitmap(int width, int height, bool isred, bool tfbit); + + // Creates a red or blue image. + NSImage* CreateNSImage(int width, int height, bool isred); + + // Checks that the given bitmap rep is actually red or blue. + void TestImageRep(NSBitmapImageRep* imageRep, bool isred); + + // Checks that the given bitmap is actually red or blue. + void TestSkBitmap(const SkBitmap& bitmap, bool isred); + // If not red, is blue. // If not tfbit (twenty-four-bit), is 444. void ShapeHelper(int width, int height, bool isred, bool tfbit); }; -void SkiaUtilsMacTest::ShapeHelper(int width, int height, - bool isred, bool tfbit) { - SkBitmap thing; +SkBitmap SkiaUtilsMacTest::CreateSkBitmap(int width, int height, + bool isred, bool tfbit) { + SkBitmap bitmap; if (tfbit) - thing.setConfig(SkBitmap::kARGB_8888_Config, width, height); + bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); else - thing.setConfig(SkBitmap::kARGB_4444_Config, width, height); - thing.allocPixels(); + bitmap.setConfig(SkBitmap::kARGB_4444_Config, width, height); + bitmap.allocPixels(); if (isred) - thing.eraseRGB(0xff, 0, 0); + bitmap.eraseRGB(0xff, 0, 0); else - thing.eraseRGB(0, 0, 0xff); + bitmap.eraseRGB(0, 0, 0xff); - // Confirm size - NSImage* image = gfx::SkBitmapToNSImage(thing); - EXPECT_DOUBLE_EQ([image size].width, (double)width); - EXPECT_DOUBLE_EQ([image size].height, (double)height); + return bitmap; +} - // Get the color of a pixel and make sure it looks fine +NSImage* SkiaUtilsMacTest::CreateNSImage(int width, int height, bool isred) { + scoped_nsobject<NSImage> image( + [[NSImage alloc] initWithSize:NSMakeSize(width, height)]); [image lockFocus]; + if (isred) + [[NSColor colorWithDeviceRed:1.0 green:0.0 blue:0.0 alpha:1.0] set]; + else + [[NSColor colorWithDeviceRed:0.0 green:0.0 blue:1.0 alpha:1.0] set]; + NSRectFill(NSMakeRect(0, 0, width, height)); + [image unlockFocus]; + return [image.release() autorelease]; +} - int x = width > 17 ? 17 : 0; - int y = height > 17 ? 17 : 0; - NSColor* color = NSReadPixel(NSMakePoint(x, y)); +void SkiaUtilsMacTest::TestImageRep(NSBitmapImageRep* imageRep, bool isred) { + // Get the color of a pixel and make sure it looks fine + int x = [imageRep size].width > 17 ? 17 : 0; + int y = [imageRep size].height > 17 ? 17 : 0; + NSColor* color = [imageRep colorAtX:x y:y]; CGFloat red = 0, green = 0, blue = 0, alpha = 0; - [image unlockFocus]; // SkBitmapToNSImage returns a bitmap in the calibrated color space (sRGB), // while NSReadPixel returns a color in the device color space. Convert back @@ -62,7 +83,40 @@ void SkiaUtilsMacTest::ShapeHelper(int width, int height, EXPECT_GT(alpha, 0.95); } -TEST_F(SkiaUtilsMacTest, FAILS_BitmapToNSImage_RedSquare64x64) { +void SkiaUtilsMacTest::TestSkBitmap(const SkBitmap& bitmap, bool isred) { + int x = bitmap.width() > 17 ? 17 : 0; + int y = bitmap.height() > 17 ? 17 : 0; + SkColor color = bitmap.getColor(x, y); + + // Be tolerant of lossy color space conversions. + // TODO(sail): Fix color space conversion issues, http://crbug.com/79946 + if (isred) { + EXPECT_GT(SkColorGetR(color), 245u); + EXPECT_LT(SkColorGetB(color), 10u); + } else { + EXPECT_LT(SkColorGetR(color), 10u); + EXPECT_GT(SkColorGetB(color), 245u); + } + EXPECT_LT(SkColorGetG(color), 10u); + EXPECT_GT(SkColorGetA(color), 245u); +} + +void SkiaUtilsMacTest::ShapeHelper(int width, int height, + bool isred, bool tfbit) { + SkBitmap thing(CreateSkBitmap(width, height, isred, tfbit)); + + // Confirm size + NSImage* image = gfx::SkBitmapToNSImage(thing); + EXPECT_DOUBLE_EQ([image size].width, (double)width); + EXPECT_DOUBLE_EQ([image size].height, (double)height); + + EXPECT_TRUE([[image representations] count] == 1); + EXPECT_TRUE([[[image representations] lastObject] + isKindOfClass:[NSBitmapImageRep class]]); + TestImageRep([[image representations] lastObject], isred); +} + +TEST_F(SkiaUtilsMacTest, BitmapToNSImage_RedSquare64x64) { ShapeHelper(64, 64, true, true); } @@ -70,8 +124,52 @@ TEST_F(SkiaUtilsMacTest, BitmapToNSImage_BlueRectangle199x19) { ShapeHelper(199, 19, false, true); } -TEST_F(SkiaUtilsMacTest, FAILS_BitmapToNSImage_BlueRectangle444) { +TEST_F(SkiaUtilsMacTest, BitmapToNSImage_BlueRectangle444) { ShapeHelper(200, 200, false, false); } +TEST_F(SkiaUtilsMacTest, 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, NSImageRepToSkBitmap) { + int width = 10; + int height = 15; + bool isred = true; + + NSImage* image = CreateNSImage(width, height, isred); + EXPECT_EQ(1u, [[image representations] count]); + NSBitmapImageRep* imageRep = [[image representations] lastObject]; + SkBitmap bitmap(gfx::NSImageRepToSkBitmap(imageRep, [image size], false)); + TestSkBitmap(bitmap, isred); +} + } // namespace |