Files
UnrealEngine/Engine/Source/ThirdParty/MaterialX/MaterialX-1.38.10/source/MaterialXRender/Image.cpp
2025-05-18 13:04:45 +08:00

599 lines
18 KiB
C++

//
// Copyright Contributors to the MaterialX Project
// SPDX-License-Identifier: Apache-2.0
//
#include <MaterialXRender/Image.h>
#include <MaterialXRender/Types.h>
#include <MaterialXGenShader/Nodes/ConvolutionNode.h>
#include <cstring>
#include <fstream>
#include <limits>
MATERIALX_NAMESPACE_BEGIN
//
// Global functions
//
ImagePtr createUniformImage(unsigned int width, unsigned int height, unsigned int channelCount, Image::BaseType baseType, const Color4& color)
{
ImagePtr image = Image::create(width, height, channelCount, baseType);
image->createResourceBuffer();
image->setUniformColor(color);
return image;
}
ImagePtr createImageStrip(const vector<ImagePtr>& imageVec)
{
ImagePtr refImage = imageVec.empty() ? nullptr : imageVec[0];
if (!refImage)
{
return nullptr;
}
unsigned int srcWidth = refImage->getWidth();
unsigned int srcHeight = refImage->getHeight();
unsigned int destWidth = srcWidth * (unsigned int) imageVec.size();
unsigned int destHeight = srcHeight;
ImagePtr imageStrip = Image::create(destWidth, destHeight, refImage->getChannelCount(), refImage->getBaseType());
imageStrip->createResourceBuffer();
unsigned int srcRowStride = refImage->getRowStride();
unsigned int destRowStride = imageStrip->getRowStride();
for (unsigned int i = 0; i < imageVec.size(); i++)
{
ConstImagePtr srcImage = imageVec[i];
if (!srcImage ||
srcImage->getWidth() != srcWidth ||
srcImage->getHeight() != srcHeight ||
srcImage->getChannelCount() != refImage->getChannelCount() ||
srcImage->getBaseType() != refImage->getBaseType())
{
throw Exception("Source images must have identical resolutions and formats in createImageStrip");
}
unsigned int xOffset = i * srcRowStride;
uint8_t* src = (uint8_t*) srcImage->getResourceBuffer();
uint8_t* dest = (uint8_t*) imageStrip->getResourceBuffer() + xOffset;
for (unsigned int y = 0; y < srcHeight; y++)
{
memcpy(dest, src, srcRowStride);
src += srcRowStride;
dest += destRowStride;
}
}
return imageStrip;
}
UnsignedIntPair getMaxDimensions(const vector<ImagePtr>& imageVec)
{
UnsignedIntPair maxSize(0, 0);
for (ImagePtr image : imageVec)
{
maxSize.first = std::max(maxSize.first, image->getWidth());
maxSize.second = std::max(maxSize.second, image->getHeight());
}
return maxSize;
}
//
// Image methods
//
Image::Image(unsigned int width, unsigned int height, unsigned int channelCount, BaseType baseType) :
_width(width),
_height(height),
_channelCount(channelCount),
_baseType(baseType),
_resourceBuffer(nullptr),
_resourceBufferDeallocator(nullptr),
_resourceId(0)
{
}
Image::~Image()
{
releaseResourceBuffer();
}
unsigned int Image::getBaseStride() const
{
if (_baseType == BaseType::FLOAT)
{
return 4;
}
if (_baseType == BaseType::HALF ||
_baseType == BaseType::UINT16 ||
_baseType == BaseType::INT16)
{
return 2;
}
if (_baseType == BaseType::UINT8 ||
_baseType == BaseType::INT8)
{
return 1;
}
throw Exception("Unsupported base type in getBaseStride");
}
unsigned int Image::getMaxMipCount() const
{
return (unsigned int) std::log2(std::max(_width, _height)) + 1;
}
void Image::setTexelColor(unsigned int x, unsigned int y, const Color4& color)
{
if (x >= _width || y >= _height)
{
throw Exception("Invalid coordinates in setTexelColor");
}
if (!_resourceBuffer)
{
throw Exception("Invalid resource buffer in setTexelColor");
}
unsigned int writeChannels = std::min(_channelCount, (unsigned int) 4);
if (_baseType == BaseType::FLOAT)
{
float* data = static_cast<float*>(_resourceBuffer) + (y * _width + x) * _channelCount;
for (unsigned int c = 0; c < writeChannels; c++)
{
data[c] = color[c];
}
}
else if (_baseType == BaseType::HALF)
{
Half* data = static_cast<Half*>(_resourceBuffer) + (y * _width + x) * _channelCount;
for (unsigned int c = 0; c < writeChannels; c++)
{
data[c] = (Half) color[c];
}
}
else if (_baseType == BaseType::UINT16)
{
uint16_t* data = static_cast<uint16_t*>(_resourceBuffer) + (y * _width + x) * _channelCount;
for (unsigned int c = 0; c < writeChannels; c++)
{
data[c] = (uint16_t) std::round(color[c] * (float) std::numeric_limits<uint16_t>::max());
}
}
else if (_baseType == BaseType::INT16)
{
int16_t* data = static_cast<int16_t*>(_resourceBuffer) + (y * _width + x) * _channelCount;
for (unsigned int c = 0; c < writeChannels; c++)
{
data[c] = (int16_t) std::round(color[c] * (float) std::numeric_limits<int16_t>::max());
}
}
else if (_baseType == BaseType::UINT8)
{
uint8_t* data = static_cast<uint8_t*>(_resourceBuffer) + (y * _width + x) * _channelCount;
for (unsigned int c = 0; c < writeChannels; c++)
{
data[c] = (uint8_t) std::round(color[c] * (float) std::numeric_limits<uint8_t>::max());
}
}
else if (_baseType == BaseType::INT8)
{
int8_t* data = static_cast<int8_t*>(_resourceBuffer) + (y * _width + x) * _channelCount;
for (unsigned int c = 0; c < writeChannels; c++)
{
data[c] = (int8_t) std::round(color[c] * (float) std::numeric_limits<int8_t>::max());
}
}
else
{
throw Exception("Unsupported base type in setTexelColor");
}
}
Color4 Image::getTexelColor(unsigned int x, unsigned int y) const
{
if (x >= _width || y >= _height)
{
throw Exception("Invalid coordinates in getTexelColor");
}
if (!_resourceBuffer)
{
throw Exception("Invalid resource buffer in getTexelColor");
}
if (_baseType == BaseType::FLOAT)
{
float* data = static_cast<float*>(_resourceBuffer) + (y * _width + x) * _channelCount;
if (_channelCount == 4)
{
return Color4(data[0], data[1], data[2], data[3]);
}
else if (_channelCount == 3)
{
return Color4(data[0], data[1], data[2], 1.0f);
}
else if (_channelCount == 2)
{
return Color4(data[0], data[1], 0.0f, 1.0f);
}
else if (_channelCount == 1)
{
return Color4(data[0], data[0], data[0], 1.0f);
}
else
{
throw Exception("Unsupported channel count in getTexelColor");
}
}
else if (_baseType == BaseType::HALF)
{
Half* data = static_cast<Half*>(_resourceBuffer) + (y * _width + x) * _channelCount;
if (_channelCount == 4)
{
return Color4(data[0], data[1], data[2], data[3]);
}
else if (_channelCount == 3)
{
return Color4(data[0], data[1], data[2], 1.0f);
}
else if (_channelCount == 2)
{
return Color4(data[0], data[1], 0.0f, 1.0f);
}
else if (_channelCount == 1)
{
return Color4(data[0], data[0], data[0], 1.0f);
}
else
{
throw Exception("Unsupported channel count in getTexelColor");
}
}
else if (_baseType == BaseType::UINT16)
{
uint16_t* data = static_cast<uint16_t*>(_resourceBuffer) + (y * _width + x) * _channelCount;
const float MAX_VALUE = (float) std::numeric_limits<uint16_t>::max();
if (_channelCount == 4)
{
return Color4(data[0] / MAX_VALUE, data[1] / MAX_VALUE, data[2] / MAX_VALUE, data[3] / MAX_VALUE);
}
else if (_channelCount == 3)
{
return Color4(data[0] / MAX_VALUE, data[1] / MAX_VALUE, data[2] / MAX_VALUE, 1.0f);
}
else if (_channelCount == 2)
{
return Color4(data[0] / MAX_VALUE, data[1] / MAX_VALUE, 0.0f, 1.0f);
}
else if (_channelCount == 1)
{
float scalar = data[0] / MAX_VALUE;
return Color4(scalar, scalar, scalar, 1.0f);
}
else
{
throw Exception("Unsupported channel count in getTexelColor");
}
}
else if (_baseType == BaseType::INT16)
{
int16_t* data = static_cast<int16_t*>(_resourceBuffer) + (y * _width + x) * _channelCount;
const float MAX_VALUE = (float) std::numeric_limits<int16_t>::max();
if (_channelCount == 4)
{
return Color4(data[0] / MAX_VALUE, data[1] / MAX_VALUE, data[2] / MAX_VALUE, data[3] / MAX_VALUE);
}
else if (_channelCount == 3)
{
return Color4(data[0] / MAX_VALUE, data[1] / MAX_VALUE, data[2] / MAX_VALUE, 1.0f);
}
else if (_channelCount == 2)
{
return Color4(data[0] / MAX_VALUE, data[1] / MAX_VALUE, 0.0f, 1.0f);
}
else if (_channelCount == 1)
{
float scalar = data[0] / MAX_VALUE;
return Color4(scalar, scalar, scalar, 1.0f);
}
else
{
throw Exception("Unsupported channel count in getTexelColor");
}
}
else if (_baseType == BaseType::UINT8)
{
uint8_t* data = static_cast<uint8_t*>(_resourceBuffer) + (y * _width + x) * _channelCount;
const float MAX_VALUE = (float) std::numeric_limits<uint8_t>::max();
if (_channelCount == 4)
{
return Color4(data[0] / MAX_VALUE, data[1] / MAX_VALUE, data[2] / MAX_VALUE, data[3] / MAX_VALUE);
}
else if (_channelCount == 3)
{
return Color4(data[0] / MAX_VALUE, data[1] / MAX_VALUE, data[2] / MAX_VALUE, 1.0f);
}
else if (_channelCount == 2)
{
return Color4(data[0] / MAX_VALUE, data[1] / MAX_VALUE, 0.0f, 1.0f);
}
else if (_channelCount == 1)
{
float scalar = data[0] / MAX_VALUE;
return Color4(scalar, scalar, scalar, 1.0f);
}
else
{
throw Exception("Unsupported channel count in getTexelColor");
}
}
else if (_baseType == BaseType::INT8)
{
int8_t* data = static_cast<int8_t*>(_resourceBuffer) + (y * _width + x) * _channelCount;
const float MAX_VALUE = (float) std::numeric_limits<int8_t>::max();
if (_channelCount == 4)
{
return Color4(data[0] / MAX_VALUE, data[1] / MAX_VALUE, data[2] / MAX_VALUE, data[3] / MAX_VALUE);
}
else if (_channelCount == 3)
{
return Color4(data[0] / MAX_VALUE, data[1] / MAX_VALUE, data[2] / MAX_VALUE, 1.0f);
}
else if (_channelCount == 2)
{
return Color4(data[0] / MAX_VALUE, data[1] / MAX_VALUE, 0.0f, 1.0f);
}
else if (_channelCount == 1)
{
float scalar = data[0] / MAX_VALUE;
return Color4(scalar, scalar, scalar, 1.0f);
}
else
{
throw Exception("Unsupported channel count in getTexelColor");
}
}
else
{
throw Exception("Unsupported base type in getTexelColor");
}
}
Color4 Image::getAverageColor()
{
Color4 averageColor;
for (unsigned int y = 0; y < getHeight(); y++)
{
for (unsigned int x = 0; x < getWidth(); x++)
{
averageColor += getTexelColor(x, y);
}
}
unsigned int sampleCount = getWidth() * getHeight();
averageColor /= (float) sampleCount;
return averageColor;
}
bool Image::isUniformColor(Color4* uniformColor)
{
Color4 refColor = getTexelColor(0, 0);
for (unsigned int y = 0; y < getHeight(); y++)
{
for (unsigned int x = 0; x < getWidth(); x++)
{
if (!x && !y)
{
continue;
}
if (getTexelColor(x, y) != refColor)
{
return false;
}
}
}
if (uniformColor)
{
*uniformColor = refColor;
}
return true;
}
void Image::setUniformColor(const Color4& color)
{
for (unsigned int y = 0; y < getHeight(); y++)
{
for (unsigned int x = 0; x < getWidth(); x++)
{
setTexelColor(x, y, color);
}
}
}
void Image::applyMatrixTransform(const Matrix33& mat)
{
for (unsigned int y = 0; y < getHeight(); y++)
{
for (unsigned int x = 0; x < getWidth(); x++)
{
Color4 color = getTexelColor(x, y);
Vector3 vec(color[0], color[1], color[2]);
vec = mat.multiply(vec);
setTexelColor(x, y, Color4(vec[0], vec[1], vec[2], color[3]));
}
}
}
void Image::applyGammaTransform(float gamma)
{
for (unsigned int y = 0; y < getHeight(); y++)
{
for (unsigned int x = 0; x < getWidth(); x++)
{
Color4 color = getTexelColor(x, y);
Vector3 vec(color[0], color[1], color[2]);
vec[0] = std::pow(std::max(vec[0], 0.0f), gamma);
vec[1] = std::pow(std::max(vec[1], 0.0f), gamma);
vec[2] = std::pow(std::max(vec[2], 0.0f), gamma);
setTexelColor(x, y, Color4(vec[0], vec[1], vec[2], color[3]));
}
}
}
ImagePtr Image::copy(unsigned int channelCount, BaseType baseType) const
{
ImagePtr newImage = Image::create(getWidth(), getHeight(), channelCount, baseType);
newImage->createResourceBuffer();
for (int y = 0; y < (int) getHeight(); y++)
{
for (int x = 0; x < (int) getWidth(); x++)
{
newImage->setTexelColor(x, y, getTexelColor(x, y));
}
}
return newImage;
}
ImagePtr Image::applyBoxBlur()
{
ImagePtr blurImage = Image::create(getWidth(), getHeight(), getChannelCount(), getBaseType());
blurImage->createResourceBuffer();
for (int y = 0; y < (int) getHeight(); y++)
{
for (int x = 0; x < (int) getWidth(); x++)
{
Color4 blurColor;
for (int dy = -1; dy <= 1; dy++)
{
int sy = std::min(std::max(y + dy, 0), (int) getHeight() - 1);
for (int dx = -1; dx <= 1; dx++)
{
int sx = std::min(std::max(x + dx, 0), (int) getWidth() - 1);
blurColor += getTexelColor(sx, sy);
}
}
blurColor /= 9.0f;
blurImage->setTexelColor(x, y, blurColor);
}
}
return blurImage;
}
ImagePtr Image::applyGaussianBlur()
{
ImagePtr blurImage1 = Image::create(getWidth(), getHeight(), getChannelCount(), getBaseType());
ImagePtr blurImage2 = Image::create(getWidth(), getHeight(), getChannelCount(), getBaseType());
blurImage1->createResourceBuffer();
blurImage2->createResourceBuffer();
for (int y = 0; y < (int) getHeight(); y++)
{
for (int x = 0; x < (int) getWidth(); x++)
{
Color4 blurColor;
unsigned int weightIndex = 0;
for (int dy = -3; dy <= 3; dy++, weightIndex++)
{
int sy = std::min(std::max(y + dy, 0), (int) getHeight() - 1);
blurColor += getTexelColor(x, sy) * GAUSSIAN_KERNEL_7[weightIndex];
}
blurImage1->setTexelColor(x, y, blurColor);
}
}
for (int y = 0; y < (int) getHeight(); y++)
{
for (int x = 0; x < (int) getWidth(); x++)
{
Color4 blurColor;
unsigned int weightIndex = 0;
for (int dx = -3; dx <= 3; dx++, weightIndex++)
{
int sx = std::min(std::max(x + dx, 0), (int) getWidth() - 1);
blurColor += blurImage1->getTexelColor(sx, y) * GAUSSIAN_KERNEL_7[weightIndex];
}
blurImage2->setTexelColor(x, y, blurColor);
}
}
return blurImage2;
}
ImagePair Image::splitByLuminance(float luminance)
{
ImagePtr underflowImage = Image::create(getWidth(), getHeight(), getChannelCount(), getBaseType());
ImagePtr overflowImage = Image::create(getWidth(), getHeight(), getChannelCount(), getBaseType());
underflowImage->createResourceBuffer();
overflowImage->createResourceBuffer();
for (unsigned int y = 0; y < getHeight(); y++)
{
for (unsigned int x = 0; x < getWidth(); x++)
{
Color4 envColor = getTexelColor(x, y);
Color4 underflowColor(
std::min(envColor[0], luminance),
std::min(envColor[1], luminance),
std::min(envColor[2], luminance), 1.0f);
Color4 overflowColor(
std::max(envColor[0] - underflowColor[0], 0.0f),
std::max(envColor[1] - underflowColor[1], 0.0f),
std::max(envColor[2] - underflowColor[2], 0.0f), 1.0f);
underflowImage->setTexelColor(x, y, underflowColor);
overflowImage->setTexelColor(x, y, overflowColor);
}
}
return std::make_pair(underflowImage, overflowImage);
}
void Image::writeTable(const FilePath& filePath, unsigned int channel)
{
std::ofstream ofs(filePath.asString());
ofs << "X Y Z" << std::endl;
for (unsigned int y = 0; y < getHeight(); y++)
{
for (unsigned int x = 0; x < getWidth(); x++)
{
double dx = ((double) x + 0.5) / (double) getWidth();
double dy = ((double) y + 0.5) / (double) getHeight();
double dz = getTexelColor(x, y)[channel];
ofs << dx << " " << dy << " " << dz << std::endl;
}
}
}
void Image::createResourceBuffer()
{
releaseResourceBuffer();
_resourceBuffer = malloc(_width * _height * _channelCount * getBaseStride());
_resourceBufferDeallocator = nullptr;
}
void Image::releaseResourceBuffer()
{
if (_resourceBuffer)
{
if (_resourceBufferDeallocator)
{
_resourceBufferDeallocator(_resourceBuffer);
}
else
{
free(_resourceBuffer);
}
_resourceBuffer = nullptr;
}
}
MATERIALX_NAMESPACE_END