blob: c962a8fe8c3a9a8a6b12f7c9c5095126d2e728dc [file] [log] [blame]
//
// Copyright 2023 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// LoadToNative_unittest.cpp: Unit tests for pixel loading functions.
#include <gmock/gmock.h>
#include <vector>
#include "common/debug.h"
#include "common/mathutil.h"
#include "image_util/loadimage.h"
using namespace angle;
using namespace testing;
namespace
{
template <typename Type>
void initializeRGBInput(std::vector<Type> &rgbInput,
size_t width,
size_t height,
size_t depth,
size_t inputPixelBytes,
size_t inputByteOffset,
size_t inputRowPitch,
size_t inputDepthPitch)
{
ASSERT(rgbInput.size() == inputDepthPitch * depth + inputByteOffset);
for (size_t z = 0; z < depth; z++)
{
for (size_t y = 0; y < height; y++)
{
for (size_t x = 0; x < width; x++)
{
size_t inputIndex =
inputByteOffset + z * inputDepthPitch + y * inputRowPitch + x * inputPixelBytes;
rgbInput[inputIndex] = x % 256;
rgbInput[inputIndex + 1] = y % 256;
rgbInput[inputIndex + 2] = z % 256;
}
}
}
}
template <typename Type, uint8_t fourthValue>
void verifyRGBToRGBAResults(const char *strCase,
std::vector<Type> &rgbInput,
std::vector<Type> &rgbaOutput,
size_t width,
size_t height,
size_t depth,
size_t inputPixelBytes,
size_t inputByteOffset,
size_t inputRowPitch,
size_t inputDepthPitch,
size_t outputPixelBytes,
size_t outputByteOffset,
size_t outputRowPitch,
size_t outputDepthPitch)
{
ASSERT(rgbInput.size() == inputDepthPitch * depth + inputByteOffset);
ASSERT(rgbaOutput.size() == outputDepthPitch * depth + outputByteOffset);
for (size_t z = 0; z < depth; z++)
{
for (size_t y = 0; y < height; y++)
{
for (size_t x = 0; x < width; x++)
{
size_t inputIndex =
inputByteOffset + z * inputDepthPitch + y * inputRowPitch + x * inputPixelBytes;
size_t outputIndex = outputByteOffset + z * outputDepthPitch + y * outputRowPitch +
x * outputPixelBytes;
bool rMatch = rgbInput[inputIndex] == rgbaOutput[outputIndex];
bool gMatch = rgbInput[inputIndex + 1] == rgbaOutput[outputIndex + 1];
bool bMatch = rgbInput[inputIndex + 2] == rgbaOutput[outputIndex + 2];
bool aMatch = rgbaOutput[outputIndex + 3] == fourthValue;
EXPECT_TRUE(rMatch && gMatch && bMatch && aMatch)
<< "Case " << strCase << ": Mismatch at Index (" << x << ", " << y << ", " << z
<< ")" << std::endl
<< "Expected output: (" << static_cast<uint32_t>(rgbInput[inputIndex]) << ", "
<< static_cast<uint32_t>(rgbInput[inputIndex + 1]) << ", "
<< static_cast<uint32_t>(rgbInput[inputIndex + 2]) << ", "
<< static_cast<uint32_t>(fourthValue) << ")" << std::endl
<< "Actual output: (" << static_cast<uint32_t>(rgbaOutput[outputIndex]) << ", "
<< static_cast<uint32_t>(rgbaOutput[outputIndex + 1]) << ", "
<< static_cast<uint32_t>(rgbaOutput[outputIndex + 2]) << ", "
<< static_cast<uint32_t>(rgbaOutput[outputIndex + 3]) << ")";
}
}
}
}
template <uint8_t fourthValue>
void TestLoadUbyteRGBToRGBA(ImageLoadContext &context,
const char *strCase,
size_t width,
size_t height,
size_t depth,
size_t inputByteOffset,
size_t outputByteOffset,
size_t inputRowAlignment)
{
constexpr uint8_t kInitialByteValue = 0xAA;
size_t inputPixelBytes = 3;
size_t inputRowPitch = rx::roundUpPow2(width * inputPixelBytes, inputRowAlignment);
size_t inputDepthPitch = height * inputRowPitch;
size_t inputActualBytes = depth * inputDepthPitch;
size_t outputPixelBytes = 4;
size_t outputRowPitch = width * outputPixelBytes;
size_t outputDepthPitch = height * outputRowPitch;
size_t outputActualBytes = depth * outputDepthPitch;
// Prepare the RGB input and RGBA output for copy. The offset values are used to add unused
// bytes to the beginning of the input and output data, in order to test address alignments.
std::vector<uint8_t> rgbInput(inputByteOffset + inputActualBytes, kInitialByteValue);
initializeRGBInput<uint8_t>(rgbInput, width, height, depth, inputPixelBytes, inputByteOffset,
inputRowPitch, inputDepthPitch);
std::vector<uint8_t> rgbaOutput(outputByteOffset + outputActualBytes, kInitialByteValue);
// Call loading function.
LoadToNative3To4<uint8_t, fourthValue>(
context, width, height, depth, rgbInput.data() + inputByteOffset, inputRowPitch,
inputDepthPitch, rgbaOutput.data() + outputByteOffset, outputRowPitch, outputDepthPitch);
// Compare the input and output data.
verifyRGBToRGBAResults<uint8_t, fourthValue>(
strCase, rgbInput, rgbaOutput, width, height, depth, inputPixelBytes, inputByteOffset,
inputRowPitch, inputDepthPitch, outputPixelBytes, outputByteOffset, outputRowPitch,
outputDepthPitch);
}
template <uint8_t fourthValue>
void TestLoadSbyteRGBToRGBA(ImageLoadContext &context,
const char *strCase,
size_t width,
size_t height,
size_t depth,
size_t inputByteOffset,
size_t outputByteOffset,
size_t inputRowAlignment)
{
constexpr int8_t kInitialByteValue = 0xAA;
size_t inputPixelBytes = 3;
size_t inputRowPitch = rx::roundUpPow2(width * inputPixelBytes, inputRowAlignment);
size_t inputDepthPitch = height * inputRowPitch;
size_t inputActualBytes = depth * inputDepthPitch;
size_t outputPixelBytes = 4;
size_t outputRowPitch = width * outputPixelBytes;
size_t outputDepthPitch = height * outputRowPitch;
size_t outputActualBytes = depth * outputDepthPitch;
// Prepare the RGB input and RGBA output for copy. The offset values are used to add unused
// bytes to the beginning of the input and output data, in order to test address alignments.
std::vector<int8_t> rgbInput(inputByteOffset + inputActualBytes, kInitialByteValue);
initializeRGBInput<int8_t>(rgbInput, width, height, depth, inputPixelBytes, inputByteOffset,
inputRowPitch, inputDepthPitch);
std::vector<int8_t> rgbaOutput(outputByteOffset + outputActualBytes, kInitialByteValue);
// Call loading function.
LoadToNative3To4<int8_t, fourthValue>(
context, width, height, depth,
reinterpret_cast<uint8_t *>(rgbInput.data() + inputByteOffset), inputRowPitch,
inputDepthPitch, reinterpret_cast<uint8_t *>(rgbaOutput.data() + outputByteOffset),
outputRowPitch, outputDepthPitch);
// Compare the input and output data.
verifyRGBToRGBAResults<int8_t, fourthValue>(strCase, rgbInput, rgbaOutput, width, height, depth,
inputPixelBytes, inputByteOffset, inputRowPitch,
inputDepthPitch, outputPixelBytes, outputByteOffset,
outputRowPitch, outputDepthPitch);
}
void TestLoadByteRGBToRGBAForAllCases(ImageLoadContext &context,
size_t inputCase,
size_t width,
size_t height,
size_t depth,
size_t inputByteOffset,
size_t outputByteOffset,
size_t inputRowAlignment)
{
std::string strCaseUFF = "UFF_" + std::to_string(inputCase);
TestLoadUbyteRGBToRGBA<0xFF>(context, strCaseUFF.c_str(), width, height, depth, inputByteOffset,
outputByteOffset, inputRowAlignment);
std::string strCaseU01 = "U01_" + std::to_string(inputCase);
TestLoadUbyteRGBToRGBA<0x01>(context, strCaseU01.c_str(), width, height, depth, inputByteOffset,
outputByteOffset, inputRowAlignment);
std::string strCaseS7F = "S7F_" + std::to_string(inputCase);
TestLoadSbyteRGBToRGBA<0x7F>(context, strCaseS7F.c_str(), width, height, depth, inputByteOffset,
outputByteOffset, inputRowAlignment);
std::string strCaseS01 = "S01_" + std::to_string(inputCase);
TestLoadSbyteRGBToRGBA<0x01>(context, strCaseS01.c_str(), width, height, depth, inputByteOffset,
outputByteOffset, inputRowAlignment);
}
// Tests the ubyte (0xFF) RGB to RGBA loading function for one RGB pixel.
TEST(LoadToNative3To4, LoadUbyteRGBToRGBADataOnePixelWithFourthCompOfFF)
{
ImageLoadContext context;
uint8_t rgbInput[] = {1, 2, 3};
uint8_t rgbaOutput[] = {0, 0, 0, 0};
constexpr uint8_t kFourthValue = 0xFF;
LoadToNative3To4<uint8_t, kFourthValue>(context, 1, 1, 1, rgbInput, 3, 3, rgbaOutput, 4, 4);
EXPECT_TRUE(rgbaOutput[0] == rgbInput[0] && rgbaOutput[1] == rgbInput[1] &&
rgbaOutput[2] == rgbInput[2] && rgbaOutput[3] == kFourthValue)
<< "Pixel mismatch";
}
// Tests the ubyte (0x01) RGB to RGBA loading function for one RGB pixel.
TEST(LoadToNative3To4, LoadUbyteRGBToRGBADataOnePixelWithFourthCompOf01)
{
ImageLoadContext context;
uint8_t rgbInput[] = {1, 2, 3};
uint8_t rgbaOutput[] = {0, 0, 0, 0};
constexpr uint8_t kFourthValue = 0x01;
LoadToNative3To4<uint8_t, kFourthValue>(context, 1, 1, 1, rgbInput, 3, 3, rgbaOutput, 4, 4);
EXPECT_TRUE(rgbaOutput[0] == rgbInput[0] && rgbaOutput[1] == rgbInput[1] &&
rgbaOutput[2] == rgbInput[2] && rgbaOutput[3] == kFourthValue)
<< "Pixel mismatch";
}
// Tests the sbyte (0x7F) RGB to RGBA loading function for one RGB pixel.
TEST(LoadToNative3To4, LoadSbyteRGBToRGBADataOnePixelWithFourthCompOf7F)
{
ImageLoadContext context;
int8_t rgbInput[] = {1, 2, 3};
int8_t rgbaOutput[] = {0, 0, 0, 0};
constexpr uint8_t kFourthValue = 0x01;
LoadToNative3To4<int8_t, kFourthValue>(context, 1, 1, 1, reinterpret_cast<uint8_t *>(rgbInput),
3, 3, reinterpret_cast<uint8_t *>(rgbaOutput), 4, 4);
EXPECT_TRUE(rgbaOutput[0] == rgbInput[0] && rgbaOutput[1] == rgbInput[1] &&
rgbaOutput[2] == rgbInput[2] && rgbaOutput[3] == static_cast<int8_t>(kFourthValue))
<< "Pixel mismatch";
}
// Tests the sbyte (0x01) RGB to RGBA loading function for one RGB pixel.
TEST(LoadToNative3To4, LoadSbyteRGBToRGBADataOnePixelWithFourthCompOf01)
{
ImageLoadContext context;
int8_t rgbInput[] = {1, 2, 3};
int8_t rgbaOutput[] = {0, 0, 0, 0};
constexpr uint8_t kFourthValue = 0x01;
LoadToNative3To4<int8_t, kFourthValue>(context, 1, 1, 1, reinterpret_cast<uint8_t *>(rgbInput),
3, 3, reinterpret_cast<uint8_t *>(rgbaOutput), 4, 4);
EXPECT_TRUE(rgbaOutput[0] == rgbInput[0] && rgbaOutput[1] == rgbInput[1] &&
rgbaOutput[2] == rgbInput[2] && rgbaOutput[3] == static_cast<int8_t>(kFourthValue))
<< "Pixel mismatch";
}
// Tests the ubyte (0xFF) RGB to RGBA loading function for 4 RGB pixels, which should be read
// together.
TEST(LoadToNative3To4, LoadUbyteRGBToRGBADataFourPixelsWithFourthCompOfFF)
{
ImageLoadContext context;
constexpr uint8_t kPixelCount = 4;
std::vector<uint8_t> rgbInput(kPixelCount * 3);
std::vector<uint8_t> rgbaOutput(kPixelCount * 4, 0);
size_t index = 0;
for (auto &inputComponent : rgbInput)
{
inputComponent = ++index;
}
constexpr uint8_t kFourthValue = 0xFF;
LoadToNative3To4<uint8_t, kFourthValue>(context, 4, 1, 1, rgbInput.data(), 3, 3,
rgbaOutput.data(), 4, 4);
for (index = 0; index < kPixelCount; index++)
{
EXPECT_TRUE(rgbaOutput[index * 4] == rgbInput[index * 3] &&
rgbaOutput[index * 4 + 1] == rgbInput[index * 3 + 1] &&
rgbaOutput[index * 4 + 2] == rgbInput[index * 3 + 2] &&
rgbaOutput[index * 4 + 3] == kFourthValue)
<< "Mismatch at pixel " << index;
}
}
// Tests the ubyte (0x01) RGB to RGBA loading function for 4 RGB pixels, which should be read
// together.
TEST(LoadToNative3To4, LoadUbyteRGBToRGBADataFourPixelsWithFourthCompOf01)
{
ImageLoadContext context;
constexpr uint8_t kPixelCount = 4;
std::vector<uint8_t> rgbInput(kPixelCount * 3);
std::vector<uint8_t> rgbaOutput(kPixelCount * 4, 0);
size_t index = 0;
for (auto &inputComponent : rgbInput)
{
inputComponent = ++index;
}
constexpr uint8_t kFourthValue = 0x01;
LoadToNative3To4<uint8_t, kFourthValue>(context, 4, 1, 1, rgbInput.data(), 3, 3,
rgbaOutput.data(), 4, 4);
for (index = 0; index < kPixelCount; index++)
{
EXPECT_TRUE(rgbaOutput[index * 4] == rgbInput[index * 3] &&
rgbaOutput[index * 4 + 1] == rgbInput[index * 3 + 1] &&
rgbaOutput[index * 4 + 2] == rgbInput[index * 3 + 2] &&
rgbaOutput[index * 4 + 3] == kFourthValue)
<< "Mismatch at pixel " << index;
}
}
// Tests the sbyte (0x7F) RGB to RGBA loading function for 4 RGB pixels, which should be read
// together.
TEST(LoadToNative3To4, LoadSbyteRGBToRGBADataFourPixelsWithFourthCompOf7F)
{
ImageLoadContext context;
constexpr uint8_t kPixelCount = 4;
std::vector<int8_t> rgbInput(kPixelCount * 3);
std::vector<int8_t> rgbaOutput(kPixelCount * 4, 0);
size_t index = 0;
for (auto &inputComponent : rgbInput)
{
inputComponent = ++index;
}
constexpr uint8_t kFourthValue = 0x01;
LoadToNative3To4<int8_t, kFourthValue>(context, 4, 1, 1,
reinterpret_cast<uint8_t *>(rgbInput.data()), 3, 3,
reinterpret_cast<uint8_t *>(rgbaOutput.data()), 4, 4);
for (index = 0; index < kPixelCount; index++)
{
EXPECT_TRUE(rgbaOutput[index * 4] == rgbInput[index * 3] &&
rgbaOutput[index * 4 + 1] == rgbInput[index * 3 + 1] &&
rgbaOutput[index * 4 + 2] == rgbInput[index * 3 + 2] &&
rgbaOutput[index * 4 + 3] == static_cast<int8_t>(kFourthValue))
<< "Mismatch at pixel " << index;
}
}
// Tests the sbyte (0x01) RGB to RGBA loading function for 4 RGB pixels, which should be read
// together.
TEST(LoadToNative3To4, LoadSbyteRGBToRGBADataFourPixelsWithFourthCompOf01)
{
ImageLoadContext context;
constexpr uint8_t kPixelCount = 4;
std::vector<int8_t> rgbInput(kPixelCount * 3);
std::vector<int8_t> rgbaOutput(kPixelCount * 4, 0);
size_t index = 0;
for (auto &inputComponent : rgbInput)
{
inputComponent = ++index;
}
constexpr uint8_t kFourthValue = 0x01;
LoadToNative3To4<int8_t, kFourthValue>(context, 4, 1, 1,
reinterpret_cast<uint8_t *>(rgbInput.data()), 3, 3,
reinterpret_cast<uint8_t *>(rgbaOutput.data()), 4, 4);
for (index = 0; index < kPixelCount; index++)
{
EXPECT_TRUE(rgbaOutput[index * 4] == rgbInput[index * 3] &&
rgbaOutput[index * 4 + 1] == rgbInput[index * 3 + 1] &&
rgbaOutput[index * 4 + 2] == rgbInput[index * 3 + 2] &&
rgbaOutput[index * 4 + 3] == static_cast<int8_t>(kFourthValue))
<< "Mismatch at pixel " << index;
}
}
// Tests the byte RGB to RGBA loading function when the width is 4-byte aligned. This loading
// function can copy 4 bytes at a time in a row.
TEST(LoadToNative3To4, LoadByteRGBToRGBADataAlignedWidth)
{
ImageLoadContext context;
size_t alignedTestWidths[] = {4, 20, 128, 1000, 4096};
for (auto &width : alignedTestWidths)
{
ASSERT(width % 4 == 0);
TestLoadByteRGBToRGBAForAllCases(context, width, width, 3, 1, 0, 0, 1);
}
}
// Tests the byte RGB to RGBA loading function when the width is not 4-byte aligned, which will
// cause the loading function to copy some bytes in the beginning and end of some rows individually.
TEST(LoadToNative3To4, LoadByteRGBToRGBADataUnalignedWidth)
{
ImageLoadContext context;
size_t unalignedTestWidths[] = {5, 22, 127, 1022, 4097};
for (auto &width : unalignedTestWidths)
{
ASSERT(width % 4 != 0);
TestLoadByteRGBToRGBAForAllCases(context, width, width, 3, 1, 0, 0, 1);
}
}
// Tests the byte RGB to RGBA loading function when there is depth.
TEST(LoadToNative3To4, LoadByteRGBToRGBADataWithDepth)
{
ImageLoadContext context;
size_t unalignedTestDepths[] = {3};
for (auto &depth : unalignedTestDepths)
{
TestLoadByteRGBToRGBAForAllCases(context, depth, 3, 3, depth, 0, 0, 1);
}
}
// Tests the byte RGB to RGBA loading function when the width is less than 4 bytes. Therefore the
// loading function will copy data one byte at a time.
TEST(LoadToNative3To4, LoadByteRGBToRGBADataWidthLessThanUint32)
{
ImageLoadContext context;
size_t smallTestWidths[] = {1, 2, 3};
for (auto &width : smallTestWidths)
{
TestLoadByteRGBToRGBAForAllCases(context, width, width, 3, 1, 0, 0, 1);
}
}
// Tests the byte RGB to RGBA loading function when when the width is 4-byte-aligned and the input
// address has an offset.
TEST(LoadToNative3To4, LoadByteRGBToRGBAWithAlignedWidthAndInputAddressOffset)
{
ImageLoadContext context;
size_t inputOffsetList[] = {1, 2, 3};
for (auto &inputOffset : inputOffsetList)
{
TestLoadByteRGBToRGBAForAllCases(context, inputOffset, 8, 8, 1, inputOffset, 0, 1);
}
}
// Tests the byte RGB to RGBA loading function when when the width is not 4-byte-aligned and the
// input address has an offset.
TEST(LoadToNative3To4, LoadByteRGBToRGBAWithUnalignedWidthAndInputAddressOffset)
{
ImageLoadContext context;
size_t inputOffsetList[] = {1, 2, 3};
for (auto &inputOffset : inputOffsetList)
{
TestLoadByteRGBToRGBAForAllCases(context, inputOffset, 7, 7, 1, inputOffset, 0, 1);
}
}
// Tests the byte RGB to RGBA loading function when the width is 4-byte-aligned and the output
// address has an offset.
TEST(LoadToNative3To4, LoadByteRGBToRGBAWithAlignedWidthAndOutputAddressOffset)
{
ImageLoadContext context;
size_t outputOffsetList[] = {1, 2, 3};
for (auto &outputOffset : outputOffsetList)
{
TestLoadByteRGBToRGBAForAllCases(context, outputOffset, 8, 8, 1, 0, outputOffset, 1);
}
}
// Tests the byte RGB to RGBA loading function when the width is not 4-byte-aligned and the output
// address has an offset.
TEST(LoadToNative3To4, LoadByteRGBToRGBAWithUnalignedWidthAndOutputAddressOffset)
{
ImageLoadContext context;
size_t outputOffsetList[] = {1, 2, 3};
for (auto &outputOffset : outputOffsetList)
{
TestLoadByteRGBToRGBAForAllCases(context, outputOffset, 7, 7, 1, 0, outputOffset, 1);
}
}
// Tests the byte RGB to RGBA loading function when the width is 4-byte-aligned and the input row
// alignment is 4.
TEST(LoadToNative3To4, LoadByteRGBToRGBAWithAlignedWidthAndAlignment4)
{
ImageLoadContext context;
size_t inputRowAlignmentList[] = {4};
for (auto &alignment : inputRowAlignmentList)
{
TestLoadByteRGBToRGBAForAllCases(context, alignment, 4, 4, 1, 0, 0, alignment);
}
}
// Tests the byte RGB to RGBA loading function when the width is not 4-byte-aligned and the input
// row alignment is 4.
TEST(LoadToNative3To4, LoadByteRGBToRGBAWithUnalignedWidthAndAlignment4)
{
ImageLoadContext context;
size_t inputRowAlignmentList[] = {4};
for (auto &alignment : inputRowAlignmentList)
{
TestLoadByteRGBToRGBAForAllCases(context, alignment, 5, 5, 1, 0, 0, alignment);
}
}
} // namespace