// 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. #include "skia/ext/skia_utils_mac.h" #import #include "base/mac/foundation_util.h" #include "base/mac/scoped_nsobject.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkCanvas.h" #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" 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 image. NSImage* CreateNSImage(int width, int height); // Checks that the given bitmap rep is actually red or blue. void TestImageRep(NSBitmapImageRep* imageRep, bool isred); // Checks that the given bitmap is red. void TestSkBitmap(const SkBitmap& bitmap); enum BitLockerTest { TestIdentity = 0, TestTranslate = 1, TestClip = 2, TestXClip = TestTranslate | TestClip, TestNoBits = 4, TestTranslateNoBits = TestTranslate | TestNoBits, TestClipNoBits = TestClip | TestNoBits, TestXClipNoBits = TestXClip | TestNoBits, }; void RunBitLockerTest(BitLockerTest test); // If not red, is blue. // If not tfbit (twenty-four-bit), is 444. void ShapeHelper(int width, int height, bool isred, bool tfbit); }; SkBitmap SkiaUtilsMacTest::CreateSkBitmap(int width, int height, bool isred, bool tfbit) { SkColorType ct = tfbit ? kN32_SkColorType : kARGB_4444_SkColorType; SkImageInfo info = SkImageInfo::Make(width, height, ct, kPremul_SkAlphaType); SkBitmap bitmap; bitmap.allocPixels(info); if (isred) bitmap.eraseARGB(0xff, 0xff, 0, 0); else bitmap.eraseARGB(0xff, 0, 0, 0xff); return bitmap; } NSImage* SkiaUtilsMacTest::CreateNSImage(int width, int height) { base::scoped_nsobject bitmap([[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil pixelsWide:width pixelsHigh:height bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace bitmapFormat:0 bytesPerRow:4 * width bitsPerPixel:32]); { gfx::ScopedNSGraphicsContextSaveGState scopedGState; [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]]; CGFloat comps[] = {1.0, 0.0, 0.0, 1.0}; NSColor* color = [NSColor colorWithColorSpace:[NSColorSpace genericRGBColorSpace] components:comps count:4]; [color set]; NSRectFill(NSMakeRect(0, 0, width, height)); } base::scoped_nsobject image( [[NSImage alloc] initWithSize:NSMakeSize(width, height)]); [image addRepresentation:bitmap]; return [image.release() autorelease]; } 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; // SkBitmapToNSImage returns a bitmap in the calibrated color space (sRGB), // while NSReadPixel returns a color in the device color space. Convert back // to the calibrated color space before testing. color = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; [color getRed:&red green:&green blue:&blue alpha:&alpha]; // Be tolerant of floating point rounding and lossy color space conversions. if (isred) { EXPECT_GT(red, 0.95); EXPECT_LT(blue, 0.05); } else { EXPECT_LT(red, 0.05); EXPECT_GT(blue, 0.95); } EXPECT_LT(green, 0.05); EXPECT_GT(alpha, 0.95); } void SkiaUtilsMacTest::TestSkBitmap(const SkBitmap& bitmap) { int x = bitmap.width() > 17 ? 17 : 0; int y = bitmap.height() > 17 ? 17 : 0; SkColor color = bitmap.getColor(x, y); EXPECT_EQ(255u, SkColorGetR(color)); EXPECT_EQ(0u, SkColorGetB(color)); EXPECT_EQ(0u, SkColorGetG(color)); EXPECT_EQ(255u, SkColorGetA(color)); } // setBitmapDevice has been deprecated/removed. Is this test still useful? void SkiaUtilsMacTest::RunBitLockerTest(BitLockerTest test) { const unsigned width = 2; const unsigned height = 2; const unsigned storageSize = width * height; const unsigned original[] = {0xFF333333, 0xFF666666, 0xFF999999, 0xFFCCCCCC}; EXPECT_EQ(storageSize, sizeof(original) / sizeof(original[0])); unsigned bits[storageSize]; memcpy(bits, original, sizeof(original)); SkImageInfo info = SkImageInfo::MakeN32Premul(width, height); SkBitmap bitmap; bitmap.installPixels(info, bits, info.minRowBytes()); SkCanvas canvas(bitmap); if (test & TestTranslate) canvas.translate(width / 2, 0); if (test & TestClip) { SkRect clipRect = {0, height / 2, width, height}; canvas.clipRect(clipRect); } { skia::SkiaBitLocker bitLocker(&canvas); CGContextRef cgContext = bitLocker.cgContext(); CGColorRef testColor = CGColorGetConstantColor(kCGColorWhite); CGContextSetFillColorWithColor(cgContext, testColor); CGRect cgRect = {{0, 0}, {width, height}}; CGContextFillRect(cgContext, cgRect); if (test & TestNoBits) { if (test & TestClip) { SkRect clipRect = {0, height / 2, width, height}; canvas.clipRect(clipRect); } } } const unsigned results[][storageSize] = { {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}, // identity {0xFF333333, 0xFFFFFFFF, 0xFF999999, 0xFFFFFFFF}, // translate {0xFF333333, 0xFF666666, 0xFFFFFFFF, 0xFFFFFFFF}, // clip {0xFF333333, 0xFF666666, 0xFF999999, 0xFFFFFFFF} // translate | clip }; for (unsigned index = 0; index < storageSize; index++) EXPECT_EQ(results[test & ~TestNoBits][index], bits[index]); } void SkiaUtilsMacTest::ShapeHelper(int width, int height, bool isred, bool tfbit) { SkBitmap thing(CreateSkBitmap(width, height, isred, tfbit)); // Confirm size NSImage* image = skia::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(base::mac::ObjCCastStrict( [[image representations] lastObject]), isred); } TEST_F(SkiaUtilsMacTest, BitmapToNSImage_RedSquare64x64) { ShapeHelper(64, 64, true, true); } TEST_F(SkiaUtilsMacTest, BitmapToNSImage_BlueRectangle199x19) { ShapeHelper(199, 19, false, true); } TEST_F(SkiaUtilsMacTest, BitmapToNSImage_BlueRectangle444) { ShapeHelper(200, 200, false, false); } TEST_F(SkiaUtilsMacTest, BitmapToNSBitmapImageRep_BlueRectangle20x30) { int width = 20; int height = 30; SkBitmap bitmap(CreateSkBitmap(width, height, false, true)); NSBitmapImageRep* imageRep = skia::SkBitmapToNSBitmapImageRep(bitmap); EXPECT_DOUBLE_EQ(width, [imageRep size].width); EXPECT_DOUBLE_EQ(height, [imageRep size].height); TestImageRep(imageRep, false); } TEST_F(SkiaUtilsMacTest, NSImageRepToSkBitmap) { int width = 10; int height = 15; NSImage* image = CreateNSImage(width, height); EXPECT_EQ(1u, [[image representations] count]); NSBitmapImageRep* imageRep = base::mac::ObjCCastStrict( [[image representations] lastObject]); NSColorSpace* colorSpace = [NSColorSpace genericRGBColorSpace]; SkBitmap bitmap(skia::NSImageRepToSkBitmapWithColorSpace( imageRep, [image size], false, [colorSpace CGColorSpace])); TestSkBitmap(bitmap); } TEST_F(SkiaUtilsMacTest, BitLocker_Identity) { RunBitLockerTest(SkiaUtilsMacTest::TestIdentity); } TEST_F(SkiaUtilsMacTest, BitLocker_Translate) { RunBitLockerTest(SkiaUtilsMacTest::TestTranslate); } TEST_F(SkiaUtilsMacTest, BitLocker_Clip) { RunBitLockerTest(SkiaUtilsMacTest::TestClip); } TEST_F(SkiaUtilsMacTest, BitLocker_XClip) { RunBitLockerTest(SkiaUtilsMacTest::TestXClip); } TEST_F(SkiaUtilsMacTest, BitLocker_NoBits) { RunBitLockerTest(SkiaUtilsMacTest::TestNoBits); } TEST_F(SkiaUtilsMacTest, BitLocker_TranslateNoBits) { RunBitLockerTest(SkiaUtilsMacTest::TestTranslateNoBits); } TEST_F(SkiaUtilsMacTest, BitLocker_ClipNoBits) { RunBitLockerTest(SkiaUtilsMacTest::TestClipNoBits); } TEST_F(SkiaUtilsMacTest, BitLocker_XClipNoBits) { RunBitLockerTest(SkiaUtilsMacTest::TestXClipNoBits); } } // namespace