2009.03.27 22:42 "[Tiff] TIFFOpen throws access error", by

2009.04.14 15:52 "Re: [Tiff] writing in-memory tiffs", by Christian Henning

Thanks Jason, for your answer. Most of my ideas I took from the libtiff tutorial on the IBM site:

http://www.ibm.com/developerworks/linux/library/l-libtiff2/

At the very bottom there is some code for writing in-memory tiff images. They also don't implement the the size procedure. Just returning the offset value. Their seek procedure also looks wrongs.

Now. I finally realized that tif_stream.cxx already implements my procedures. I think Bob, pointed that out a while ago but I didn't pick that one up. I finally have something running for writing in-memory images using stringstream. I have attached my code below.

I believe there are two bugs in tif_stream.cxx. For some reasons libtiff is try to seek before writing anything. This has two ramifications:

  1. _tiffosSeekProc: The seek will fail and wont be recovered by the workaround. Meaning the following write will fail. I put an if statement fixing the problem. Please see below in my seek proc.
  2. _tiffosWriteProc: When writing to an empty buffer ( pos is -1 ). The return value is incorrect ( os->tellp() - pos ). I fix that with another if statement, as you can see below.

The newest beta of libtiff4 still doesn't work. The console output is as follows:

seek: 0
write: 8
seek: 0
error: Maximum TIFF file size exceeded

Regards,
Christian

#include <stdio.h>

#include <cassert>
#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>

extern "C" {
#include "tiff.h"
#include "tiffio.h"
}

using namespace std;

static ostream* get_ostream( thandle_t handle )
{
    return reinterpret_cast< ostream* >( handle );
}

static tsize_t read_proc( thandle_t fd
                        , tdata_t   buf
                        , tsize_t   size

                        )
{
    return 0;
}

static tsize_t write_proc( thandle_t handle
                         , tdata_t   buf
                         , tsize_t   size

                         )
{
    cout << "write: " << size << endl;

ostream* os = get_ostream( handle );

    ios::pos_type pos = os->tellp();

    os->write( reinterpret_cast< const char* >( buf )

             , static_cast<streamsize>( size )
             );

    // Bug Fix
    if( static_cast< streamsize >( pos ) == -1 )
    {
        return size;
    }
    else
    {
        return static_cast< tsize_t >( os->tellp() - pos );
    }

}

static toff_t seek_proc( thandle_t handle
                       , toff_t off

                       , int way
                       )
{

    cout << "seek: " << off << endl;

    ostream* os = get_ostream( handle );

    if( os->fail() )
        return static_cast< toff_t >( -1 );

    // Bug Fix
    if( static_cast< streamsize >( os->tellp() ) == -1 )
        return static_cast< toff_t >( 0 );

    os->seekp( off
             , way == SEEK_SET
               ? ios::beg
               : ( way == SEEK_CUR
                   ? ios::cur
                   : ios::end
                 )
              );

    if( os->fail() )
    {
        ios::iostate old_state;
        ios::pos_type origin;

        old_state = os->rdstate();

        // reset the fail bit or else tellp() won't work below
        os->clear(os->rdstate() & ~ios::failbit);
        switch( way )
        {
            case SEEK_SET:
            {
                origin = 0;

                break;
            }

            case SEEK_CUR:
            {
                origin = os->tellp();

                break;
            }

            case SEEK_END:
            {
                os->seekp( 0, ios::end );
                origin = os->tellp();

                break;
            }

        }

        // restore original stream state
        os->clear( old_state );

        if( ( static_cast< size_t >( origin ) + off ) > 0 )
        {
            os->clear( os->rdstate() & ~ios::failbit );

            os->seekp( 0, ios::end );
            size_t num_fill = static_cast< size_t >( origin ) + off -
os->tellp();

            for( size_t i = 0; i < num_fill; i++ )
            {
                os->put( '\0' );
            }

            os->seekp( static_cast< toff_t >( origin ) + off, ios::beg );
        }
    }

    return static_cast< toff_t >( os->tellp() );

}

static int close_proc( thandle_t handle )
{

    // Our stream was not allocated by us,
    // so it shouldn't be closed by us.

    return 0;
}

static toff_t size_proc( thandle_t handle )
{

    ostream* os = get_ostream( handle );

    ios::pos_type pos = os->tellp();
    ios::pos_type len;
    os->seekp( 0, ios::end );
    len = os->tellp();

    os->seekp( pos );

    return static_cast< toff_t >( len );

}

static void tiff_error( const char* module, const char* fmt, va_list ap )
{
    char buf[1000];
    sprintf(buf, fmt, ap);
    cout << "error: " << buf << endl;
}

static void tiff_warning( char const *module, char const *fmt, va_list ap )
{
    char buf[1000];
    sprintf(buf, fmt, ap);
    cout << "warning: " << fmt << endl;
}

int main()
{

 stringstream ss( ios_base::in | ios_base::out | ios_base::binary );

        TIFFSetErrorHandler  ( tiff_error   );
        TIFFSetWarningHandler( tiff_warning );

    TIFF* tiff = TIFFClientOpen( "dummy"
                               , "w"

                                   , &ss

, read_proc
                            , write_proc
                            , seek_proc
                            , close_proc
                            , size_proc
                            , NULL
                            , NULL
                            );

    TIFFSetField( tiff, TIFFTAG_IMAGEWIDTH, 100 );

        TIFFSetField( tiff, TIFFTAG_IMAGELENGTH , 1 );
        TIFFSetField( tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG );
        TIFFSetField( tiff, TIFFTAG_SAMPLESPERPIXEL, 1 );
        TIFFSetField( tiff, TIFFTAG_BITSPERSAMPLE, 8 );
        TIFFSetField( tiff, TIFFTAG_SAMPLEFORMAT, PHOTOMETRIC_RGB );
        TIFFSetField( tiff, TIFFTAG_COMPRESSION, COMPRESSION_NONE );

    vector< unsigned char > buffer( 300 );

        fill( buffer.begin(), buffer.end(), 0 );

            TIFFWriteScanline( tiff, &buffer.front(), 0, 0 );

        TIFFClose( tiff );

        ofstream out( "c:\\in-memory.tiff", ios_base::binary );
        out << ss.rdbuf();

    return 0;
}