2005.01.25 18:29 "[Tiff] issues with TIFFStreamOpen", by Michael Rinne

2005.01.25 18:29 "[Tiff] issues with TIFFStreamOpen", by Michael Rinne

Hi,

first of all, thanks to Edward Lam for adding C++ iostream support to LibTIFF!

Unfortunately, I have got to report two issues for the iostream support. However, I have got at least a fix for one issue.

1. LibTIFF currently overwrites any data already written to an ostream.

It looks like the LibTIFF uses the seek function right at the start in order to seek to the beginning of the file. This happens in TIFFClientOpen by a call to _tiffosSeekProc with off=0 and whence=SEEK_SET. Therefore, any data written to the stream before calling TIFFStreamOpen gets overwritten by the TIFF image.

The fix for this is straight forward, because it is analogous to the implementation when reading a TIFF image from an istream. The modified file tif_stream.cxx is attached to this mail.

2. LibTIFF is currently unable to write images to an ostringstream.

This might be Microsoft Visual C++ specific. I am using MS VC 7.1. If one calls tellp on a newly constructed ostringstream object it returns the position -1. According to a posting to microsoft.public.vc.stl this might be the correct behavior for the basic_ostream object, see

http://groups.google.de/groups?selm=uMWFsM4EBHA.1676%40tkmsftngp04&output=gplain

The LibTIFF dumps the error 'Error writing TIFF header.' in this case. I tried to work around this issue by simply writing a few characters to the ostringstream. This results almost in a valid stream, but it fails at the end with the error message 'Error writing data for field "BitsPerSample".'. I tracked it down to the macro SeekOK which gets called in tif_dirwrite.c at line 1066. This macro calls _tiffosSeekProc with an offset that seems to exceed the amount of data written to the stream and therefore the ostream method returns a bad offset.

This behavior is the same for the original tif_stream.cxx file and the patched version attached to this mail.

I hope this provides enough information for a TIFF guru to help overcome this issue. Please give me a hint on how to fix this problem.

Thanks,
Michael

/* $Id: tif_stream.cxx,v 1.2 2004/11/13 11:19:15 dron Exp $ */

/*
 * Copyright (c) 1988-1996 Sam Leffler
 * Copyright (c) 1991-1996 Silicon Graphics, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and
 * its documentation for any purpose is hereby granted without fee, provided
 * that (i) the above copyright notices and this permission notice appear in
 * all copies of the software and related documentation, and (ii) the names of
 * Sam Leffler and Silicon Graphics may not be used in any advertising or
 * publicity relating to the software without the specific, prior written
 * permission of Sam Leffler and Silicon Graphics.
 *
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
 *
 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 * OF THIS SOFTWARE.
 */

/*
 * TIFF Library UNIX-specific Routines.
 */
#include <iostream>
#include "tiffiop.h"

using namespace std;

class tiffis_data
{
  public:

        istream *myIS;
        long    myStreamStartPos;
};

static tsize_t
_tiffosReadProc(thandle_t, tdata_t, tsize_t)
{
        return 0;
}

static tsize_t
_tiffisReadProc(thandle_t fd, tdata_t buf, tsize_t size)
{
        tiffis_data     *data = (tiffis_data *)fd;

        data->myIS->read((char *)buf, (int)size);

        return data->myIS->gcount();
}

static tsize_t
_tiffosWriteProc(thandle_t fd, tdata_t buf, tsize_t size)
{
        ostream *os = (ostream *)fd;
        int     pos = os->tellp();

        os->write((const char *)buf, size);

        return ((int)os->tellp()) - pos;
}

static tsize_t
_tiffisWriteProc(thandle_t, tdata_t, tsize_t)
{
        return 0;
}

static toff_t
_tiffosSeekProc(thandle_t fd, toff_t off, int whence)
{
        ostream *os = (ostream *)fd;

        switch(whence) {
        case SEEK_SET:
                os->seekp(off, ios::beg);
                break;
        case SEEK_CUR:
                os->seekp(off, ios::cur);
                break;
        case SEEK_END:
                os->seekp(off, ios::end);
                break;
        }

        return os->tellp();
}

static toff_t
_tiffisSeekProc(thandle_t fd, toff_t off, int whence)
{
        tiffis_data     *data = (tiffis_data *)fd;

        switch(whence) {
        case SEEK_SET:
                data->myIS->seekg(data->myStreamStartPos + off, ios::beg);
                break;
        case SEEK_CUR:
                data->myIS->seekg(off, ios::cur);
                break;
        case SEEK_END:
                data->myIS->seekg(off, ios::end);
                break;
        }

        return ((long)data->myIS->tellg()) - data->myStreamStartPos;
}

static toff_t
_tiffosSizeProc(thandle_t fd)
{
        ostream *os = (ostream *)fd;
        int     pos = os->tellp();
        int     len;

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

        return len;
}

static toff_t
_tiffisSizeProc(thandle_t fd)
{
        tiffis_data     *data = (tiffis_data *)fd;
        int             pos = data->myIS->tellg();
        int             len;

        data->myIS->seekg(0, ios::end);
        len = data->myIS->tellg();
        data->myIS->seekg(pos);

        return len;
}

static int
_tiffosCloseProc(thandle_t fd)
{
        // Our stream was not allocated by us, so it shouldn't be closed by us.
        return 0;
}

static int
_tiffisCloseProc(thandle_t fd)
{
        // Our stream was not allocated by us, so it shouldn't be closed by us.
        delete (tiffis_data *)fd;
        return 0;
}

static int
_tiffDummyMapProc(thandle_t , tdata_t* , toff_t* )
{
        return (0);
}

static void
_tiffDummyUnmapProc(thandle_t , tdata_t , toff_t )
{
}

/*
 * Open a TIFF file descriptor for read/writing.
 */
static TIFF*
_tiffStreamOpen(const char* name, const char* mode, void *fd)
{
        TIFF*   tif;

        if( strchr(mode, 'w') ) {
            // Open for writing.
            tif = TIFFClientOpen(name, mode,
                (thandle_t) fd,
                _tiffosReadProc, _tiffosWriteProc,
                _tiffosSeekProc, _tiffosCloseProc, _tiffosSizeProc,
                _tiffDummyMapProc, _tiffDummyUnmapProc);
        } else {
            tiffis_data *data = new tiffis_data;
            data->myIS = (istream *)fd;
            data->myStreamStartPos = data->myIS->tellg();
            // Open for reading.
            tif = TIFFClientOpen(name, mode,
                (thandle_t) data,
                _tiffisReadProc, _tiffisWriteProc,
                _tiffisSeekProc, _tiffisCloseProc, _tiffisSizeProc,
                _tiffDummyMapProc, _tiffDummyUnmapProc);
        }

        return (tif);
}

TIFF*
TIFFStreamOpen(const char* name, ostream *os)
{
        // NB: We don't support mapped files with streams
        return _tiffStreamOpen(name, "wm", os);
}

TIFF*
TIFFStreamOpen(const char* name, istream *is)
{
        // NB: We don't support mapped files with streams
        return _tiffStreamOpen(name, "rm", is);
}

/* vim: set ts=8 sts=8 sw=8 noet: */