blob: 226b61dc97cd0033b1dcb03a922d223a02c61818 [file] [log] [blame]
// 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 <stddef.h>
#include "base/mac/scoped_nsobject.h"
#include "base/macros.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][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());
}
ImageMacTest(const ImageMacTest&) = delete;
ImageMacTest& operator=(const ImageMacTest&) = delete;
};
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);
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);
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::ArePNGBytesCloseToBitmap(
*bytes1x, image_skia.GetRepresentation(1.0f).GetBitmap(),
gt::MaxColorSpaceConversionColorShift()));
EXPECT_TRUE(gt::ArePNGBytesCloseToBitmap(
*bytes2x, image_skia.GetRepresentation(2.0f).GetBitmap(),
gt::MaxColorSpaceConversionColorShift()));
}
} // namespace