Displaying numbers in a kernel

One of the problems with Pixel Bender is that it’s always been difficult to debug. This kernel helps out a little – it provides a way to display integers and floats on screen.

The value you’re trying to display must be the same for each invocation of the kernel – that means that it can’t depend on outCoord in any way. If you’re trying to debug something that only happens at a particular position you’ll need to hard code that position in for testing.

There are two functions – one for displaying integers and one for displaying floats:

bool
displayInt( int n, int2 topLeft, int scale, int width, bool displayleadingZeros );

bool
displayFloat( float f, int2 topLeft, int scale, int width, int precision, bool displayleadingZeros );

Each of them returns a bool – if the return value is true the pixel is part of the number to be displayed and should be highlighted in some manner.

The kernel here exposes most of the options as parameters just to make it easy to play around with them – in practice just set them to reasonable constant values.

<languageVersion : 1.0;>
kernel NumberDisplay
<   
    namespace : "AIF Test";
    vendor : "Adobe";
    version : 1;
    description : "Display numbers";
>
{
    input image4 src;
    output pixel4 dst;

    parameter int intValue
    <
        minValue : -150;
        maxValue : 50;
        defaultValue: 10;
    >;

    parameter float floatValue
    <
        minValue : -15.0;
        maxValue : 15.0;
        defaultValue: 0.0;
    >;

    parameter bool displayleadingZerosParam
    <
        defaultValue: false;
    >;
        
    parameter int intScale
    <
        minValue : 3;
        maxValue : 10;
        defaultValue: 5;
    >;
    
    parameter int nDigitsParam
    <
        minValue : 5;
        maxValue : 10;
        defaultValue: 5;
    >;
    
    parameter int floatWidth
    <
        minValue : 10;
        maxValue : 20;
        defaultValue: 10;
    >;
    
    parameter int floatPrecision
    <
        minValue : 2;
        maxValue : 7;
        defaultValue: 2;
    >;
    
    parameter int floatScale
    <
        minValue : 3;
        maxValue : 10;
        defaultValue: 5;
    >;
    
    
    const int digitXSpacing = 4;

    bool
    hitPosition( float4x4 m, int2 p )
    {
        return m[ p[ 1 ] ][ p[ 0 ] ] > 0.5;
    }

    bool displayNegativeSign( int digit, int2 gridPosition )
    {
        bool result = false;

        if( gridPosition.x >= 0 && gridPosition.x < 3 && gridPosition.y >= 0 && gridPosition.y < 5 )
        {
                int offset1D = gridPosition.y * 3 + gridPosition.x;
                
                int2 matrixPosition = int2( int( mod( float( offset1D ), 4.0 ) ), offset1D / 4 );

                result = hitPosition( float4x4( 0, 0, 0,  0, 0, 0,  1, 1, 1,  0, 0, 0,  0, 0, 0,  0 ), matrixPosition );
        }

        return result;
    }
    
    bool displayDecimalPoint( int2 gridPosition )
    {
        bool result = false;

        if( gridPosition.x >= 0 && gridPosition.x < 3 && gridPosition.y >= 0 && gridPosition.y < 5 )
        {
                int offset1D = gridPosition.y * 3 + gridPosition.x;
                
                int2 matrixPosition = int2( int( mod( float( offset1D ), 4.0 ) ), offset1D / 4 );

                result = hitPosition( float4x4( 0, 0, 0,  0, 0, 0,  0, 0, 0,  0, 0, 0,  0, 1, 0,  0 ), matrixPosition );
        }

        return result;
    }
    
    bool displayInt( int n, int digit, int2 gridPosition, int width, bool displayleadingZeros )
    {
        bool result = false;

        for( int i = 0; i < width - ( digit + 1 ); ++i )
        {
            n /= 10;
        }

        if( n != 0 || digit == width - 1 || displayleadingZeros )
        {
            n = int( mod( float( n ), 10.0 ) );

            int offset1D = gridPosition.y * 3 + gridPosition.x;
            
            int2 matrixPosition = int2( int( mod( float( offset1D ), 4.0 ) ), offset1D / 4 );

            if( n == 0 ) result = hitPosition( float4x4( 1, 1, 1,  1, 0, 1,  1, 0, 1,  1, 0, 1,  1, 1, 1, 0 ), matrixPosition );
            if( n == 1 ) result = hitPosition( float4x4( 0, 0, 1,  0, 0, 1,  0, 0, 1,  0, 0, 1,  0, 0, 1, 0 ), matrixPosition );
            if( n == 2 ) result = hitPosition( float4x4( 1, 1, 1,  0, 0, 1,  1, 1, 1,  1, 0, 0,  1, 1, 1, 0 ), matrixPosition );
            if( n == 3 ) result = hitPosition( float4x4( 1, 1, 1,  0, 0, 1,  1, 1, 1,  0, 0, 1,  1, 1, 1, 0 ), matrixPosition );
            if( n == 4 ) result = hitPosition( float4x4( 1, 0, 1,  1, 0, 1,  1, 1, 1,  0, 0, 1,  0, 0, 1, 0 ), matrixPosition );
            if( n == 5 ) result = hitPosition( float4x4( 1, 1, 1,  1, 0, 0,  1, 1, 1,  0, 0, 1,  1, 1, 1, 0 ), matrixPosition );
            if( n == 6 ) result = hitPosition( float4x4( 1, 1, 1,  1, 0, 0,  1, 1, 1,  1, 0, 1,  1, 1, 1, 0 ), matrixPosition );
            if( n == 7 ) result = hitPosition( float4x4( 1, 1, 1,  0, 0, 1,  0, 0, 1,  0, 0, 1,  0, 0, 1, 0 ), matrixPosition );
            if( n == 8 ) result = hitPosition( float4x4( 1, 1, 1,  1, 0, 1,  1, 1, 1,  1, 0, 1,  1, 1, 1, 0 ), matrixPosition );
            if( n == 9 ) result = hitPosition( float4x4( 1, 1, 1,  1, 0, 1,  1, 1, 1,  0, 0, 1,  1, 1, 1, 0 ), matrixPosition );
        }            

        return result;
    }

    int getNDigits( int n )
    {
        return int( ceil( log2( float( n + 1 ) ) / log2( 10.0 ) ) );
    }

    void
    getGridInformation( int2 topLeft, int scale, out int digit, out int2 gridPosition )
    {
            float2 offsetFromTopLeftFloat = floor( outCoord() - float2( topLeft ) );
            int2 offsetFromTopLeftInt = int2( offsetFromTopLeftFloat );
            int2 gridOffsetFromTopLeftInt = int2( floor( float2( offsetFromTopLeftInt ) / float2( scale ) ) );

            digit = gridOffsetFromTopLeftInt.x / digitXSpacing;
            gridPosition = int2( gridOffsetFromTopLeftInt.x - digit * digitXSpacing, gridOffsetFromTopLeftInt.y );
    }

    // Returns true iff this is a position that might lead to a number being displayed
    bool 
    positionNeedsChecking( int digit, int2 gridPosition, int width )
    {
        return 
            gridPosition.x >= 0 && 
            gridPosition.x < 3 && 
            gridPosition.y >= 0 && gridPosition.y < 5 
            && digit >= 0 && digit < width;
    }

    bool
    displayInt( int n, int digit, int2 gridPosition, int scale, int width, bool displayleadingZeros )
    {
        bool result = false;
        
        bool negative = n < 0;
        n = n < 0 ? -n : n;

        int negativeDigitPosition = displayleadingZeros ? 0 : width - getNDigits( n ) - 1;

        if( negative && digit == negativeDigitPosition )
        {
            result = displayNegativeSign( digit, gridPosition );
        }
        else
        {
            result = displayInt( n, digit, gridPosition, width, displayleadingZeros );
        }

        return result;
    }
    
    bool
    displayInt( int n, int2 topLeft, int scale, int width, bool displayleadingZeros )
    {
        bool result = false;

        int digit;
        int2 gridPosition;

        getGridInformation( topLeft, scale, digit, gridPosition );

        if( positionNeedsChecking( digit, gridPosition, width ) )
        {
            result = displayInt( n, digit, gridPosition, scale, width, displayleadingZeros );
        }

        return result;
    }

    bool
    displayFloat( float f, int2 topLeft, int scale, int width, int precision, bool displayleadingZeros )
    {
        bool result = false;

        int i = int( f > 0.0 ? floor( f ) : ceil( f ) );
        float fra = f > 0.0 ? ( f - float( i ) ) : ( -f + float( i ) );

        int digit;
        int2 gridPosition;

        getGridInformation( topLeft, scale, digit, gridPosition );

        if( positionNeedsChecking( digit, gridPosition, width ) )
        {
            int intWidth = width - precision - 1;

            // Display the integer value
            if( positionNeedsChecking( digit, gridPosition, intWidth ) )
                result = displayInt( i, digit, gridPosition, scale, intWidth, displayleadingZeros );

            // Display the fractional value
            if( positionNeedsChecking( digit - intWidth - 1, gridPosition, precision ) )
                result = result || 
                   displayInt( int( fra * pow( 10.0, float( precision ) ) ) , digit - intWidth - 1, gridPosition, scale, precision, true );

            // Display the decinal point
            if( digit == intWidth )
            {
                result = result || displayDecimalPoint( gridPosition );
            }
        }
        
        return result;
    }

    void
    evaluatePixel()
    {
        dst = sampleNearest( src, outCoord() );

        if( displayInt( intValue, int2( 10, 10 ), intScale, 12, displayleadingZerosParam ) )
        {
            dst = float4( 1.0, 0.0, 0.0, 1.0 );
        }

        if( displayFloat( floatValue, int2( 10, 50 ), floatScale, floatWidth, floatPrecision, displayleadingZerosParam ) )
        {
            dst = float4( 0.0, 0.0, 1.0, 1.0 );
        }
    }
}