Skip to main content
Skip table of contents

Gamma correction with Apex Tone Curves

📌 Overview

Apex tone curves provide an easy way to alter pixel values post rendering, with the work done on the GPU for improved performance.

This article shows how to use tone curves to apply gamma correction. If you want to know more about the principles of gamma correction, please see Gamma 101.

ℹ️ Key Concept

The Mako API Reference provides an excellent explanation of the tone curves feature. Search for toneCurves to locate the relevant section. The important takeaway is:

  • A tone curve is represented as a vector of floating point values interpreted as a series of output values covering the input range 0 to 1, where the first value corresponds to an input value of 0, the last corresponding to an input value of 1, and the others distributed evenly over the 0 to 1 range.

It’s clear that a gamma correction curve can be represented this way; all we need to do is generate a table of values and apply it.

⌨️ Code Examples

Generating the table of values (8-bit grayscale)

For an input value and a given gamma value γ, the output is calculated as output ═ inputγ. An Apex tone curve can be expressed with up to 4096 entries; for 8-bit grayscale, 256 is all we need. They are supplied to the tone curve function as a vector of floating point values, so this code will do the job:

CPP
// Create a renderspec. Here we choose to render to an IDOMImage
auto imageRenderSpec = CImageRenderSpec();
...

// Make  a gamma curve
CFloatVect gammaTable();
gammaTable.resize(256);
for (int x = 0; x < 256; ++x)
{
    float normalized = x / 255.0f;
    gammaTable[x] = std::pow(normalized, gammaValue);
}

// Create a CToneCurvesPostProcessSpec
auto gammaCurves = CToneCurvesPostProcessSpec::create(CEDLVector({ gammaTable }));

// Add to the post processes
imageRenderSpec.postProcesses.append(gammaCurves);

The CToneCurvesPostProcessSpec requires a table for each colorant being rendered, so a set of curves for CMYK might look like this:

CPP
// Create a CToneCurvesPostProcessSpec
auto gammaCurves = CToneCurvesPostProcessSpec::create(CEDLVector({ gammaTable, gammaTable, gammaTable, gammaTable }));

The equivalent C#:

C#
// Create a renderspec. Here we choose to render to an IDOMImage
var imageRenderSpec = new CImageRenderSpec();
...

// Make a gamma curve
var gammaTable = new float[256];
for (int x = 0; x < 256; ++x)
{
    float normalized = x / 255.0f;
    gammaTable[x] = (float)Math.Pow(normalized, gammaValue);
}

// Create a CToneCurvesPostProcessSpec
var gammaCurve = new CEDLVectFloat(gammaTable);
var gammaCurves = CToneCurvesPostProcessSpec.create(
    new CEDLVectVectFloat(new StdVectCEDLVectFloat() { gammaCurve }));

// Add to the post processes
imageRenderSpec.postProcesses.append(gammaCurves);

⌨️ Code Examples

Complete code examples will be published on GitHub shortly.

📚 Additional Resources

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.