/* GenericLuminanceToRGBA8_FormattingShader.frag.txt -- Luminance output formatter
 *
 * This shader converts a HDR luminance texture into a RGBA8 8bpc framebuffer
 * image, suitable for display with a variety of high precision luminance
 * output devices. It expects the luminance image data in the red channel of
 * the texture, with values ranging from 0.0 - 1.0, remaps it into
 * the bpc bit data range of the device (max. 16 bpc), then breaks the
 * (up to 16 bit) integral luminance index value into a low byte index (8 LSB)
 * between 0 and 255, and a high byte index (up to 8 MSB) between 0 and up
 * 255. The (LSB,MSB) index pair is used as a 2D coordinate into a 2D RGBA8
 * LUT texture of size 256 columns by up to 256 rows texels and the value
 * read from that LUT texture (a RGBA8 pixel) is written to the framebuffer. 
 *
 * By encoding a proper LUT from 0 to 2^bpc-1 luminance indices into RGBA8 drive
 * pixels, one can drive luminance output devices of up to 16 bit luminance
 * resolution. Building of the LUT texture is usually done by PsychImaging.m.
 *
 * Please note that LUT based output conversion is usually not the most
 * efficient method, as it requires two texture lookups (1 dependend texture
 * lookup) per converted pixel. This requires large memory bandwidth and is
 * limited in speed by (relatively high) memory access latency. If possible
 * for a given output device it is better to encode the conversion algorithm
 * as a closed formula inside a specific shader for that device, e.g., like
 * done for the CRS Bits+ boxes, the ATI native 10bpc framebuffer, etc.
 * The speed of numeric computation of modern GPU's is higher than their ability
 * to access memory - and therefore to access lookup tables, so one can do
 * a lot of computation in the time it would take to fetch a single value
 * from a LUT!
 *
 * This LUT shader is a generic "catch all" to quickly support new devices
 * for which no specific output driver has been written yet.
 *
 * This shader is intended for use as a plugin for the 'FinalOutputFormattingBlit'
 * chain of the Psychtoolbox-3 imaging pipeline.
 *
 * (c)2007, 2008 by Mario Kleiner, part of PTB-3, licensed to you under MIT license.
 * See file License.txt in the Psychtoolbox root folder for the license.
 *
 */

#extension GL_ARB_texture_rectangle : enable

/* The input Image - Usually from texture unit 0: */
uniform sampler2DRect Image;

/* The LUT - Usually bound to texture unit 1: */
uniform sampler2DRect LUT;

/* Maximum luminance index for device: Typically 2^bpc - 1 for given bit depths bpc: */
uniform float MaxIndex;

/* Declare external function for luminance color conversion: */
float icmTransformColor1(float incolor);

void main()
{
    vec2 lutindex;

    /* Retrieve HDR/High precision input luminance value from RED channel:      */
    /* The same value is stored (replicated) in the GREEN and BLUE channels     */
    /* if this is a drawn luminance image, so choice of channel doesn't matter. */
    /* We expect these values to be in 0.0 - 1.0 range.                         */
    /* We add a small numerical 'epsilon' offset of 1e-7 to the fetched value.  */
    /* This is to account for small numerical differences between GPU's float   */
    /* storage format and CPU's double format. This is not strictly neccessary, */
    /* but we do it to achieve identical results between GPU and CPU conversion */
    /* to simplify validation/testing and intermix of CPU and GPU methods.      */
    float incolor = texture2DRect(Image, gl_TexCoord[0].st).r + 1e-7;

    /* Apply some color transformation (clamping, gamma correction etc.): */
    incolor = icmTransformColor1(incolor);

    /* Remap 'incolor' from 0.0 - 1.0 range to range 0 - MaxIndex. Turn */
    /* remapped value into integral value by floor()'ing to closest */
    /* smaller or equal integral value: 'index' will be our linear index into the LUT. */
    float index = floor(incolor * MaxIndex);

    /* Compute high byte (8 MSBs) of 2D texture lookup position: */
    lutindex.y = floor(index / 256.0) + 0.5;

    /* Compute low byte (8 LSBs) of 2D texture lookup position: */
    lutindex.x = floor(mod(index, 256.0)) + 0.5;

    /* Readout LUT texture at 2D location lutindex(x,y) to get final RGBA8 */
    /* output pixel and write it to framebuffer: */
    gl_FragColor = texture2DRect(LUT, lutindex);
}
