Render Filter Implementation

About

Based on maxon::FilterContextInterface, maxon::FilterImageInterface and maxon::FilterInterface one can implement custom render filters.

FilterContex

A filter context is the central class to create queues, images and filters. A real-world implementation would initialize a library or a framework such as CUDA or OpenCL.

// This example shows the declaration of a FilterContext implementation.
namespace FilterContextClasses
{
MAXON_DECLARATION(maxon::FilterContextClasses::EntryType, EXAMPLECONTEXT, "net.maxonexample.render_filter.context.example");
} // FilterContextClasses
// This example shows a simple implementation of FilterContextInterface.
class FilterContextImpl : public maxon::Component<FilterContextImpl, maxon::FilterContextInterface>
{
public:
MAXON_METHOD maxon::Result<maxon::FilterImageRef> CreateImage(const maxon::DataDictionary& imageDesc);
MAXON_METHOD maxon::Result<void> ExecuteCommandQueue(maxon::FilterCommandQueueRef commandQueue);
};
{
// Initialize the FilterContext with optional parameters provided by the DataDictionary (e.g. the type of context)
return maxon::OK;
}
maxon::Result<maxon::FilterRef> FilterContextImpl::CreateFilter(const maxon::LiteralId& filterType)
{
// Create the filter of type 'filterType'.
// In a real world example the context would create some internal object and provide the FilterRef with the object, for instance.
maxon::FilterRef filter = maxon::FilterClasses::Get(filterType.Get()).Create() iferr_return;
return filter;
}
maxon::Result<maxon::FilterImageRef> FilterContextImpl::CreateImage(const maxon::DataDictionary& imageDesc)
{
// Create an image using the imageDesc DataDictionary
maxon::FilterImageRef image = maxon::FilterImageClasses::EXAMPLEIMAGE().Create() iferr_return;
// Get Image parameters...
const maxon::UInt32 width = imageDesc.Get(maxon::FilterImageDescriptionParameters::WIDTH) iferr_return;
const maxon::UInt32 height = imageDesc.Get(maxon::FilterImageDescriptionParameters::HEIGHT) iferr_return;
// ... and do sanity checking!
CheckState(width > 0 && height > 0, "Invalid Image Size!"_s);
// Get the actual implementation of our FilterImage
FilterImageImpl* imageImpl = FilterImageImpl::GetOrNull(image);
CheckState(imageImpl);
imageImpl->Init(width, height) iferr_return;
return image;
}
maxon::Result<maxon::FilterCommandQueueRef> FilterContextImpl::CreateCommandQueue()
{
// A command queue is optional and not needed in this case.
return maxon::FunctionNotImplementedError(MAXON_SOURCE_LOCATION, "No command queue support"_s);
}
maxon::Result<void> FilterContextImpl::ExecuteCommandQueue(maxon::FilterCommandQueueRef commandQueue)
{
// A command queue is optional and not supported in this case.
return maxon::FunctionNotImplementedError(MAXON_SOURCE_LOCATION, "No command queue support"_s);
}
// register implementation
MAXON_COMPONENT_CLASS_REGISTER(FilterContextImpl, maxon::FilterContextClasses::EXAMPLECONTEXT);

FilterImage

A filter image stores the given image data. Internally it might represent a data structure related to the underlying library or framework.

// This example shows the declaration of a FilterImage implementation.
namespace FilterImageClasses
{
MAXON_DECLARATION(maxon::FilterImageClasses::EntryType, EXAMPLEIMAGE, "net.maxonexample.render_filter.image.example");
} // FilterImageClasses
// This example shows a simple implementation of FilterImageInterface wrapping around a maxon::BaseArray.
class FilterImageImpl : public maxon::Component<FilterImageImpl, maxon::FilterImageInterface>
{
public:
MAXON_METHOD maxon::Result<void> WriteToFile(const maxon::Url& url);
maxon::Result<void> Init(const maxon::UInt32 width, const maxon::UInt32 height);
const maxon::BaseArray<maxon::Vector4d32>& GetImageData() const;
private:
// Here, the FilterImage just holds a BaseArray of Vector Data to represent and image.
};
maxon::Result<void> FilterImageImpl::WriteToBuffer(maxon::BaseArray<maxon::Vector4d32>& buffer)
{
return buffer.CopyFrom(_imgData);
}
maxon::Result<void> FilterImageImpl::ReadFromCPUMemory(const maxon::BaseArray<maxon::Vector4d32>& data)
{
return _imgData.CopyFrom(data);
}
maxon::Result<void> FilterImageImpl::WriteToFile(const maxon::Url& url)
{
return maxon::FunctionNotImplementedError(MAXON_SOURCE_LOCATION, "Function not implemented"_s);
}
maxon::Result<void> FilterImageImpl::Init(const maxon::UInt32 width, const maxon::UInt32 height)
{
return _imgData.Resize(width * height);
}
const maxon::BaseArray<maxon::Vector4d32>& FilterImageImpl::GetImageData() const
{
return _imgData;
}
maxon::BaseArray<maxon::Vector4d32>& FilterImageImpl::GetImageData()
{
return _imgData;
}
// register implementation
MAXON_COMPONENT_CLASS_REGISTER(FilterImageImpl, maxon::FilterImageClasses::EXAMPLEIMAGE);

Filter

The actual filter is based on maxon::FilterInterface. It can store various parameters and operates on the given data set.

// This example shows the declaration of a Filter implementation.
namespace FilterClasses
{
MAXON_DECLARATION(FilterClasses::EntryType, EXAMPLEINVERT, "net.maxonexample.render_filter.filter.example.invert");
MAXON_DECLARATION(FilterClasses::EntryType, EXAMPLEPOWER, "net.maxonexample.render_filter.filter.example.power");
} // FilterClasses
// This example shows two simple implementations of FilterInterface. Both share functionality using a ComponentRoot.
//-------------------------------------------------------------------------------------
// A custom ComponentRoot that provides a base implementation for all our filters
//-------------------------------------------------------------------------------------
class FilterComponentRoot : public maxon::ComponentRoot
{
public:
// shared implementation of Execute()
maxon::Result<void> Execute(const maxon::FilterImageRef& input, maxon::FilterImageRef output);
protected:
};
maxon::Result<void> FilterComponentRoot::Execute(const maxon::FilterImageRef& input, maxon::FilterImageRef output)
{
// Get the actual implementation of our FilterImage
FilterImageImpl* inputImpl = FilterImageImpl::GetOrNull(input);
FilterImageImpl* outputImpl = FilterImageImpl::GetOrNull(output);
CheckState(inputImpl && outputImpl);
const maxon::BaseArray<maxon::Vector4d32>& inputData = inputImpl->GetImageData();
maxon::BaseArray<maxon::Vector4d32>& outputData = outputImpl->GetImageData();
CheckState(inputData.GetCount() == outputData.GetCount(), "Input and Output image sizes do not match!"_s);
ExecuteImpl(inputData, outputData) iferr_return;
return maxon::OK;
}
//-------------------------------------------------------------------------------------
// Filter Examples
// Represents a filter that can be executed or attached to a filter command queue.
// Use the Set() function to apply settings.
//-------------------------------------------------------------------------------------
class InvertFilterImpl : public maxon::ComponentWithBase<InvertFilterImpl, FilterComponentRoot, maxon::FilterInterface>
{
public:
MAXON_METHOD maxon::Result<void> Set(const maxon::InternedId& parameter, const maxon::Data& data);
MAXON_METHOD maxon::Result<void> SetProgressMonitor(const maxon::FilterProgressMonitorFunction progressMonitor, void* userPtr);
private:
virtual maxon::Result<void> ExecuteImpl(const maxon::BaseArray<maxon::Vector4d32>& inputData, maxon::BaseArray<maxon::Vector4d32>& outputData) override;
maxon::Bool _everyPixel = true;
};
class PowerFilterImpl : public maxon::ComponentWithBase<PowerFilterImpl, FilterComponentRoot, maxon::FilterInterface>
{
public:
MAXON_METHOD maxon::Result<void> Set(const maxon::InternedId& parameter, const maxon::Data& data);
MAXON_METHOD maxon::Result<void> SetProgressMonitor(const maxon::FilterProgressMonitorFunction progressMonitor, void* userPtr);
private:
virtual maxon::Result<void> ExecuteImpl(const maxon::BaseArray<maxon::Vector4d32>& inputData, maxon::BaseArray<maxon::Vector4d32>& outputData) override;
maxon::Float32 _exponent = 1.0f;
};
maxon::Result<void> InvertFilterImpl::Set(const maxon::InternedId& parameter, const maxon::Data& data)
{
// Switch on the parameter ID
switch (ID_SWITCH(parameter))
{
case ID_CASE(maxonexample::FilterExampleParameter::EVERYPIXEL):
{
_everyPixel = data.GetOrNull<maxon::Bool>();
break;
}
}
return maxon::OK;
}
maxon::Result<void> InvertFilterImpl::SetProgressMonitor(const maxon::FilterProgressMonitorFunction progressMonitor, void* userPtr)
{
return maxon::OK;
}
maxon::Result<void> InvertFilterImpl::ExecuteImpl(const maxon::BaseArray<maxon::Vector4d32>& inputData, maxon::BaseArray<maxon::Vector4d32>& outputData)
{
for (maxon::UInt32 i = 0; i < outputData.GetCount(); ++i)
{
const maxon::Bool skip = _everyPixel == false && i % 2 == 0;
outputData[i] = skip ? inputData[i] : maxon::Vector4d32(1.0f) - inputData[i];
}
return maxon::OK;
}
maxon::Result<void> PowerFilterImpl::Set(const maxon::InternedId& parameter, const maxon::Data& data)
{
// Switch on the parameter ID
switch (ID_SWITCH(parameter))
{
case ID_CASE(maxonexample::FilterExampleParameter::EXPONENT):
{
_exponent = data.GetOrNull<maxon::Float32>();
break;
}
}
return maxon::OK;
}
maxon::Result<void> PowerFilterImpl::SetProgressMonitor(const maxon::FilterProgressMonitorFunction progressMonitor, void* userPtr)
{
return maxon::OK;
}
maxon::Result<void> PowerFilterImpl::ExecuteImpl(const maxon::BaseArray<maxon::Vector4d32>& inputData, maxon::BaseArray<maxon::Vector4d32>& outputData)
{
for (maxon::UInt32 i = 0; i < outputData.GetCount(); ++i)
{
const maxon::Vector4d32& input = inputData[i];
maxon::Vector4d32& output = outputData[i];
for (maxon::UInt32 c = 0; c < 4; ++c)
{
output[c] = maxon::Pow(input[c], _exponent);
}
}
return maxon::OK;
}
// register implementations
MAXON_COMPONENT_CLASS_REGISTER(InvertFilterImpl, maxon::FilterClasses::EXAMPLEINVERT);
MAXON_COMPONENT_CLASS_REGISTER(PowerFilterImpl, maxon::FilterClasses::EXAMPLEPOWER);

Usage

Custom filter can be used as presented:

// Create example context
maxon::FilterContextRef filterContext = maxon::FilterContextClasses::EXAMPLECONTEXT().Create() iferr_return;
filterContext.Init() iferr_return;
// Create filter
maxon::FilterRef filterInvert = filterContext.CreateFilter(maxon::FilterClasses::EXAMPLEINVERT.GetId()) iferr_return;
filterInvert.Set(maxonexample::FilterExampleParameter::EVERYPIXEL, maxon::Data(false)) iferr_return;
maxon::FilterRef filterPower = filterContext.CreateFilter(maxon::FilterClasses::EXAMPLEPOWER.GetId()) iferr_return;
filterPower.Set(maxonexample::FilterExampleParameter::EXPONENT, maxon::Data(0.42f)) iferr_return;
// Create images
maxon::DataDictionary imgDesc;
imgDesc.Set(maxon::FilterImageDescriptionParameters::WIDTH, static_cast<maxon::UInt32>(width)) iferr_return;
imgDesc.Set(maxon::FilterImageDescriptionParameters::HEIGHT, static_cast<maxon::UInt32>(height)) iferr_return;
maxon::FilterImageRef input = filterContext.CreateImage(imgDesc) iferr_return;
maxon::FilterImageRef output = filterContext.CreateImage(imgDesc) iferr_return;
// Load image data
input.ReadFromCPUMemory(bufferColor) iferr_return;
// Execute the filters
filterInvert.Execute(input, output) iferr_return;
filterPower.Execute(output, input) iferr_return;
// Get result image
input.WriteToBuffer(bufferFiltered) iferr_return;

Further Reading