blob: 10b5395eefb6e9de8de858bb036853aeeac2bc41 [file] [log] [blame]
// Copyright 2019 The Fuchsia 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 "src/camera/lib/image_utils/raw12_writer.h"
#include <array>
#include <vector>
#include <gtest/gtest.h>
namespace camera {
namespace {
// All widths/heights are in double-pixels, i.e. a width of 500 means a row of '500 *
// kBytesPerDoublePixel' bytes.
constexpr uint32_t kWidth = 2;
constexpr uint32_t kHeight = 2;
constexpr size_t kSquareImageSize = kWidth * kHeight * kBytesPerDoublePixel;
constexpr uint32_t kDoubleWidth = kWidth * 2;
constexpr uint32_t kDoubleHeight = kHeight * 2;
constexpr uint32_t kHalfWidth = kWidth / 2;
constexpr uint32_t kHalfHeight = kHeight / 2;
constexpr size_t kLongImageSize = kHalfWidth * kDoubleHeight * kBytesPerDoublePixel;
constexpr size_t kWideImageSize = kDoubleWidth * kHalfHeight * kBytesPerDoublePixel;
const std::array<uint8_t, kSquareImageSize> kSquareImage = {0x80, 0x00, 0x00, 0x80, 0x00, 0x00,
0xFF, 0x00, 0x0F, 0xFF, 0xFF, 0xFF};
const std::array<uint8_t, kLongImageSize> kLongImage = {0x80, 0x00, 0x00, 0x55, 0x00, 0x05,
0x80, 0xAA, 0xA0, 0xFF, 0x00, 0x0F};
const std::array<uint8_t, kWideImageSize> kWideImage = {0x80, 0x00, 0x00, 0x80, 0x00, 0x00,
0x80, 0x00, 0x00, 0x80, 0x00, 0x00};
constexpr uint32_t kExhaustiveWidth = 500;
constexpr uint32_t kExhaustiveHeight = 500;
constexpr uint32_t kExhaustiveImageSize =
kExhaustiveWidth * kExhaustiveHeight * kBytesPerDoublePixel;
// Padding is in bytes.
constexpr uint32_t kPadding = 1000;
TEST(Raw12WriterTest, Constructor) { Raw12Writer::Create(kWidth, kHeight); }
TEST(Raw12WriterTest, ConstructorCheckFailsWithZeroWidth) {
ASSERT_TRUE(Raw12Writer::Create(0, kHeight).is_error());
}
TEST(Raw12WriterTest, ConstructorCheckFailsWithZeroHeight) {
ASSERT_TRUE(Raw12Writer::Create(kWidth, 0).is_error());
}
// Helper method to initialize a Raw12Writer and write an image of specified width/height.
void ReadTestImage(std::vector<uint8_t>* buf, uint32_t width, uint32_t height) {
auto result = Raw12Writer::Create(width, height);
ASSERT_TRUE(result.is_ok());
auto image_writer = result.take_value();
zx_status_t status;
zx::vmo vmo;
status = image_writer->Write(&vmo, kRedPixel, kMaxVal, kMaxVal);
ASSERT_EQ(status, ZX_OK);
status = vmo.read(&buf->front(), 0, image_writer->vmo_size());
ASSERT_EQ(status, ZX_OK);
}
// Helper method to retrieve a double-pixel byte array from a specific x and y position in an image.
// Width and height provided to calculate scaling factors.
std::array<uint8_t, kBytesPerDoublePixel> GetDoublePixelAtPostion(uint32_t width, uint32_t height,
uint32_t x_pos, uint32_t y_pos) {
if (x_pos >= width || y_pos >= height) {
return std::array<uint8_t, kBytesPerDoublePixel>();
}
const uint16_t kRowStep = kMaxVal / (width - 1);
const uint16_t kHeightStep = kMaxVal / (height - 1);
uint16_t blue_pixel = static_cast<uint16_t>(kRowStep * x_pos);
uint16_t green_pixel = static_cast<uint16_t>(kHeightStep * y_pos);
return (y_pos % 2 == 0) ? PixelValuesToDoublePixel(kRedPixel, green_pixel)
: PixelValuesToDoublePixel(green_pixel, blue_pixel);
}
// Helper method to copy a target pixel from one array/vector to the other.
void CopyDoublePixel(uint8_t* target, const uint8_t* source, uint32_t target_index,
uint32_t source_index = 0) {
target[target_index] = source[source_index + 0];
target[target_index + 1] = source[source_index + 1];
target[target_index + 2] = source[source_index + 2];
}
TEST(Raw12WriterTest, WriteSquareImage) {
std::vector<uint8_t> buf(kSquareImageSize);
ASSERT_NO_FATAL_FAILURE(ReadTestImage(&buf, kWidth, kHeight));
auto expected_buf = std::vector<uint8_t>(kSquareImage.begin(), kSquareImage.end());
EXPECT_TRUE(buf == expected_buf);
}
TEST(Raw12WriterTest, WriteLongImage) {
std::vector<uint8_t> buf(kLongImageSize);
ASSERT_NO_FATAL_FAILURE(ReadTestImage(&buf, kHalfWidth, kDoubleHeight));
auto expected_buf = std::vector<uint8_t>(kLongImage.begin(), kLongImage.end());
EXPECT_TRUE(buf == expected_buf);
}
TEST(Raw12WriterTest, WriteWideImage) {
std::vector<uint8_t> buf(kWideImageSize);
ASSERT_NO_FATAL_FAILURE(ReadTestImage(&buf, kDoubleWidth, kHalfHeight));
auto expected_buf = std::vector<uint8_t>(kWideImage.begin(), kWideImage.end());
EXPECT_TRUE(buf == expected_buf);
}
TEST(Raw12WriterTest, WriteExhaustiveImage) {
std::vector<uint8_t> buf(kExhaustiveImageSize);
ASSERT_NO_FATAL_FAILURE(ReadTestImage(&buf, kExhaustiveWidth, kExhaustiveHeight));
// Test byte value at the center.
const uint32_t kCenterWidth = kExhaustiveWidth / 2;
const uint32_t kCenterHeight = kExhaustiveHeight / 2;
uint32_t index = (kExhaustiveWidth * kCenterHeight + kCenterWidth) * kBytesPerDoublePixel;
auto double_pixel =
std::array<uint8_t, kBytesPerDoublePixel>({buf[index], buf[index + 1], buf[index + 2]});
auto expected_double_pixel =
GetDoublePixelAtPostion(kExhaustiveWidth, kExhaustiveHeight, kCenterWidth, kCenterHeight);
EXPECT_TRUE(expected_double_pixel == double_pixel);
const size_t kBorderSize = kExhaustiveWidth * kBytesPerDoublePixel;
std::vector<uint8_t> expected_top_border(kBorderSize);
std::vector<uint8_t> expected_bottom_border(kBorderSize);
std::vector<uint8_t> expected_left_border(kBorderSize);
std::vector<uint8_t> expected_right_border(kBorderSize);
std::vector<uint8_t> actual_left_border(kBorderSize);
std::vector<uint8_t> actual_right_border(kBorderSize);
index = 0;
for (uint32_t i = 0; i < kExhaustiveWidth; i++) {
// Test byte values along the top and bottom rows.
expected_double_pixel = GetDoublePixelAtPostion(kExhaustiveWidth, kExhaustiveHeight, i, 0);
CopyDoublePixel(&expected_top_border[0], &expected_double_pixel[0], index);
expected_double_pixel =
GetDoublePixelAtPostion(kExhaustiveWidth, kExhaustiveHeight, i, kExhaustiveHeight - 1);
CopyDoublePixel(&expected_bottom_border[0], &expected_double_pixel[0], index);
// Test byte values along the left and right columns.
uint32_t first_column_byte_offset = i * kExhaustiveWidth * kBytesPerDoublePixel;
uint32_t last_column_byte_offset =
first_column_byte_offset + (kExhaustiveWidth - 1) * kBytesPerDoublePixel;
expected_double_pixel = GetDoublePixelAtPostion(kExhaustiveWidth, kExhaustiveHeight, 0, i);
CopyDoublePixel(&expected_left_border[0], &expected_double_pixel[0], index);
CopyDoublePixel(&actual_left_border[0], &buf[0], index, first_column_byte_offset);
expected_double_pixel =
GetDoublePixelAtPostion(kExhaustiveWidth, kExhaustiveHeight, kExhaustiveWidth - 1, i);
CopyDoublePixel(&expected_right_border[0], &expected_double_pixel[0], index);
CopyDoublePixel(&actual_right_border[0], &buf[0], index, last_column_byte_offset);
index += kBytesPerDoublePixel;
}
// std::equal needed for the first two comparisons as it automatically factors in buf size.
EXPECT_TRUE(std::equal(expected_top_border.begin(), expected_top_border.end(), buf.begin()));
EXPECT_TRUE(
std::equal(expected_bottom_border.begin(), expected_bottom_border.end(),
buf.begin() + kExhaustiveWidth * (kExhaustiveHeight - 1) * kBytesPerDoublePixel));
EXPECT_TRUE(actual_left_border == expected_left_border);
EXPECT_TRUE(actual_right_border == expected_right_border);
}
TEST(Raw12WriterTest, WriteDoesNotOverwriteBytesPastImage) {
// Add some padding to the buffer (after the image).
std::vector<uint8_t> buf(kExhaustiveImageSize + kPadding);
ASSERT_NO_FATAL_FAILURE(ReadTestImage(&buf, kExhaustiveWidth, kExhaustiveHeight));
// Compare it to a buffer of zeroes of equal length to the padding.
const std::array<uint8_t, kPadding> kExpectedBuf = {};
// Check that the last byte in the image is not 0.
EXPECT_NE(0x0, buf[kExhaustiveImageSize - 1]);
// Compares kExpectedBuf to end of buf + size of kExpectedBuf.
EXPECT_TRUE(
std::equal(kExpectedBuf.begin(), kExpectedBuf.end(), buf.begin() + kExhaustiveImageSize));
}
TEST(Raw12WriterTest, PixelValuesToDoublePixelWorksCorrectly) {
const std::array<uint8_t, kBytesPerDoublePixel> kExpectedZeroBuf = {};
const std::array<uint8_t, kBytesPerDoublePixel> kExpectedFullBuf = {0xFF, 0xFF, 0xFF};
const std::array<uint8_t, kBytesPerDoublePixel> kExpectedRandomBuf = {0x3C, 0x09, 0xD1};
EXPECT_EQ(kExpectedZeroBuf, PixelValuesToDoublePixel(0x0, 0x0));
EXPECT_EQ(kExpectedFullBuf, PixelValuesToDoublePixel(0xFFF, 0xFFF));
EXPECT_EQ(kExpectedRandomBuf, PixelValuesToDoublePixel(0x3C1, 0x09D));
}
TEST(Raw12WriterTest, DoublePixelToPixelValuesWorksCorrectly) {
const std::array<uint8_t, kBytesPerDoublePixel> kZeroBuf = {};
const std::array<uint8_t, kBytesPerDoublePixel> kFullBuf = {0xFF, 0xFF, 0xFF};
const std::array<uint8_t, kBytesPerDoublePixel> kRandomBuf = {0x3C, 0x09, 0xD1};
const std::pair<uint16_t, uint16_t> kExpectedZeroPair(0x0, 0x0);
const std::pair<uint16_t, uint16_t> kExpectedFullPair(0xFFF, 0xFFF);
const std::pair<uint16_t, uint16_t> kExpectedRandomPair(0x3C1, 0x09D);
EXPECT_EQ(kExpectedZeroPair, DoublePixelToPixelValues(kZeroBuf));
EXPECT_EQ(kExpectedFullPair, DoublePixelToPixelValues(kFullBuf));
EXPECT_EQ(kExpectedRandomPair, DoublePixelToPixelValues(kRandomBuf));
}
} // namespace
} // namespace camera