// 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 <Cocoa/Cocoa.h> #include "base/logging.h" #include "base/mac/scoped_nsobject.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/image/image.h" #include "ui/gfx/image/image_png_rep.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_skia_util_mac.h" #include "ui/gfx/image/image_unittest_util.h" namespace { // Returns true if the structure of |ns_image| matches the structure // described by |width|, |height|, and |scales|. // The structure matches if: // - |ns_image| is not nil. // - |ns_image| has NSImageReps of |scales|. // - Each of the NSImageReps has a pixel size of [|ns_image| size] * // scale. bool NSImageStructureMatches( NSImage* ns_image, int width, int height, const std::vector<float>& scales) { if (!ns_image || [ns_image size].width != width || [ns_image size].height != height || [ns_image representations].count != scales.size()) { return false; } for (size_t i = 0; i < scales.size(); ++i) { float scale = scales[i]; bool found_match = false; for (size_t j = 0; j < [ns_image representations].count; ++j) { NSImageRep* ns_image_rep = [[ns_image representations] objectAtIndex:j]; if (ns_image_rep && [ns_image_rep pixelsWide] == width * scale && [ns_image_rep pixelsHigh] == height * scale) { found_match = true; break; } } if (!found_match) return false; } return true; } void BitmapImageRep(int width, int height, NSBitmapImageRep** image_rep) { *image_rep = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:width pixelsHigh:height bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace bitmapFormat:0 bytesPerRow:0 bitsPerPixel:0] autorelease]; unsigned char* image_rep_data = [*image_rep bitmapData]; for (int i = 0; i < width * height * 3; ++i) image_rep_data[i] = 255; } class ImageMacTest : public testing::Test { public: ImageMacTest() { gfx::ImageSkia::SetSupportedScales(gfx::test::Get1xAnd2xScales()); } virtual ~ImageMacTest() { } private: DISALLOW_COPY_AND_ASSIGN(ImageMacTest); }; namespace gt = gfx::test; TEST_F(ImageMacTest, MultiResolutionNSImageToImageSkia) { const int kWidth1x = 10; const int kHeight1x = 12; const int kWidth2x = 20; const int kHeight2x = 24; NSBitmapImageRep* ns_image_rep1; BitmapImageRep(kWidth1x, kHeight1x, &ns_image_rep1); NSBitmapImageRep* ns_image_rep2; BitmapImageRep(kWidth2x, kHeight2x, &ns_image_rep2); base::scoped_nsobject<NSImage> ns_image( [[NSImage alloc] initWithSize:NSMakeSize(kWidth1x, kHeight1x)]); [ns_image addRepresentation:ns_image_rep1]; [ns_image addRepresentation:ns_image_rep2]; gfx::Image image(ns_image.release()); EXPECT_EQ(1u, image.RepresentationCount()); const gfx::ImageSkia* image_skia = image.ToImageSkia(); std::vector<float> scales; scales.push_back(1.0f); scales.push_back(2.0f); EXPECT_TRUE(gt::ImageSkiaStructureMatches(*image_skia, kWidth1x, kHeight1x, scales)); // ToImageSkia should create a second representation. EXPECT_EQ(2u, image.RepresentationCount()); } // Test that convertng to an ImageSkia from an NSImage with scale factors // other than 1x and 2x results in an ImageSkia with scale factors 1x and // 2x; TEST_F(ImageMacTest, UnalignedMultiResolutionNSImageToImageSkia) { const int kWidth1x = 10; const int kHeight1x= 12; const int kWidth4x = 40; const int kHeight4x = 48; NSBitmapImageRep* ns_image_rep4; BitmapImageRep(kWidth4x, kHeight4x, &ns_image_rep4); base::scoped_nsobject<NSImage> ns_image( [[NSImage alloc] initWithSize:NSMakeSize(kWidth1x, kHeight1x)]); [ns_image addRepresentation:ns_image_rep4]; gfx::Image image(ns_image.release()); EXPECT_EQ(1u, image.RepresentationCount()); const gfx::ImageSkia* image_skia = image.ToImageSkia(); std::vector<float> scales; scales.push_back(1.0f); scales.push_back(2.0f); EXPECT_TRUE(gt::ImageSkiaStructureMatches(*image_skia, kWidth1x, kHeight1x, scales)); // ToImageSkia should create a second representation. EXPECT_EQ(2u, image.RepresentationCount()); } TEST_F(ImageMacTest, MultiResolutionImageSkiaToNSImage) { const int kWidth1x = 10; const int kHeight1x= 12; const int kWidth2x = 20; const int kHeight2x = 24; gfx::ImageSkia image_skia; image_skia.AddRepresentation(gfx::ImageSkiaRep( gt::CreateBitmap(kWidth1x, kHeight1x), 1.0f)); image_skia.AddRepresentation(gfx::ImageSkiaRep( gt::CreateBitmap(kWidth2x, kHeight2x), 2.0f)); gfx::Image image(image_skia); EXPECT_EQ(1u, image.RepresentationCount()); EXPECT_EQ(2u, image.ToImageSkia()->image_reps().size()); NSImage* ns_image = image.ToNSImage(); std::vector<float> scales; scales.push_back(1.0f); scales.push_back(2.0f); EXPECT_TRUE(NSImageStructureMatches(ns_image, kWidth1x, kHeight1x, scales)); // Request for NSImage* should create a second representation. EXPECT_EQ(2u, image.RepresentationCount()); } TEST_F(ImageMacTest, MultiResolutionPNGToNSImage) { const int kSize1x = 25; const int kSize2x = 50; scoped_refptr<base::RefCountedMemory> bytes1x = gt::CreatePNGBytes(kSize1x); scoped_refptr<base::RefCountedMemory> bytes2x = gt::CreatePNGBytes(kSize2x); std::vector<gfx::ImagePNGRep> image_png_reps; image_png_reps.push_back(gfx::ImagePNGRep(bytes1x, 1.0f)); image_png_reps.push_back(gfx::ImagePNGRep(bytes2x, 2.0f)); gfx::Image image(image_png_reps); NSImage* ns_image = image.ToNSImage(); std::vector<float> scales; scales.push_back(1.0f); scales.push_back(2.0f); EXPECT_TRUE(NSImageStructureMatches(ns_image, kSize1x, kSize1x, scales)); // Converting from PNG to NSImage should not go through ImageSkia. EXPECT_FALSE(image.HasRepresentation(gfx::Image::kImageRepSkia)); // Convert to ImageSkia to check pixel contents of NSImageReps. gfx::ImageSkia image_skia = gfx::ImageSkiaFromNSImage(ns_image); EXPECT_TRUE(gt::IsEqual(bytes1x, image_skia.GetRepresentation(1.0f).sk_bitmap())); EXPECT_TRUE(gt::IsEqual(bytes2x, image_skia.GetRepresentation(2.0f).sk_bitmap())); } } // namespace