2008.11.24 19:40 "[Tiff] TIFFWriteScanline corrupts buffer when compression with predication is enabled", by William B Thompson

2008.11.24 19:40 "[Tiff] TIFFWriteScanline corrupts buffer when compression with predication is enabled", by William B Thompson

As best as I can tell, TIFFWriteScanline corrupts the input buffer in the calling program with LZW and deflate (zip) compression when horizontal prediction is enabled for integer values or floating point prediction is enable for floating point values. This occurs with both libtiff 3.8.2 and 4.0.0beta2. A test program for the 8 bit integer case is included at the end of this message.

The declaration of TIFFWriteScanline does not include the "const" qualifier for the buffer argument, so modifying values in the buffer is not, strictly speaking, in violation of the specification. It seems to me, however, that it is bad practice. It is certainly bad practice when not described in the man page for TIFFWriteScanline.

One might argue that it is rare to rewrite or otherwise reuse an output buffer that has just been written to a file. I can testify from personal experience that it might be rare, but it does occur (and is a pain to track down when it happens).

        - Bill

/*
 * Run with and without -p flag.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <tiffio.h>

#define OUTPUT_NAME "libtiff-prediction-test.tif"

int
main ( int argc, char *argv[] )
{

    TIFF        *output;
    uint8       *output_buffer;
    int         i;
    int         prediction_flag = 0;

    if ( ( argc == 2 ) && ( strcmp ( argv[1], "-p" ) == 0 ) ) {
        prediction_flag = 1;
    }

    output_buffer = (uint8 *) _TIFFmalloc ( 10 * sizeof ( uint8 ) );
    for ( i = 0; i < 10; i++ ) {

        output_buffer[i] = 10;
    }

    output = TIFFOpen ( OUTPUT_NAME, "w" );
    TIFFSetField ( output, TIFFTAG_IMAGEWIDTH, 10 );
    TIFFSetField ( output, TIFFTAG_IMAGELENGTH, 1 );
    TIFFSetField ( output, TIFFTAG_BITSPERSAMPLE, 8 );
    TIFFSetField ( output, TIFFTAG_SAMPLESPERPIXEL, 1 );
    TIFFSetField ( output, TIFFTAG_COMPRESSION, COMPRESSION_LZW);
    if ( prediction_flag ) {
        printf ( "using horizontal prediction\n" );
        TIFFSetField ( output, TIFFTAG_PREDICTOR, PREDICTOR_HORIZONTAL );
    } else {
        printf ( "not using horizontal prediction\n" );
    }
    TIFFSetField ( output, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
    TIFFSetField ( output, TIFFTAG_ROWSPERSTRIP,
            TIFFDefaultStripSize ( output, 0 ) );
    TIFFSetField ( output, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG );
    TIFFSetField ( output, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK );

    printf ( "output buffer before call to TIFFWriteScanline:\n" );
    for ( i = 0; i < 10; i++ ) {
        printf ( "%4d", output_buffer[i] );
    }
    printf ( "\n" );

    TIFFWriteScanline ( output, output_buffer, 0, 0 );

    printf ( "output buffer after call to TIFFWriteScanline:\n" );
    for ( i = 0; i < 10; i++ ) {
        printf ( "%4d", output_buffer[i] );
    }
    printf ( "\n" );

    TIFFClose ( output );

    return ( EXIT_SUCCESS );    /* normal exit */

}