17

Is there a way in LibTiff how I can read a file from Memory and save it to Memory?

I don't want to save the image to the disc first, before opening it with an other library...

Thanks so much!

Van Coding
  • 24,244
  • 24
  • 88
  • 132

5 Answers5

22

I know this is an old question, but I am going to post an easier, more up-to-date answer for those like myself who need this information for more recent versions of libtiff. In the newest version of libtiff (4.0.2), and even the past few versions I believe (check for your specific version number), there is an include file called tiffio.hxx. It has two extern functions for reading/writing to streams in memory:

extern TIFF* TIFFStreamOpen(const char*, std::ostream *);
extern TIFF* TIFFStreamOpen(const char*, std::istream *);

You can just include this file and read or write to memory.

Writing example:

#include <tiffio.h>
#include <tiffio.hxx>
#include <sstream>    

std::ostringstream output_TIFF_stream;

//Note: because this is an in memory TIFF, just use whatever you want for the name - we 
//aren't using it to read from a file
TIFF* mem_TIFF = TIFFStreamOpen("MemTIFF", &output_TIFF_stream);

//perform normal operations on mem_TIFF here like setting fields
//...

//Write image data to the TIFF 
//..

TIFFClose(mem_TIFF);   

//Now output_TIFF_stream has all of my image data. I can do whatever I need to with it.

Reading is very similar:

#include <tiffio.h>
#include <tiffio.hxx>
#include <sstream>

std::istringstream input_TIFF_stream;
//Populate input_TIFF_stream with TIFF image data
//...

TIFF* mem_TIFF = TIFFStreamOpen("MemTIFF", &input_TIFF_stream);

//perform normal operations on mem_TIFF here reading fields
//...

TIFFClose(mem_TIFF);

These are very simple examples, but you can see that by using TIFFStreamOpen you don't have to override those functions and pass them to TIFFClientOpen.

Darklighter
  • 2,082
  • 1
  • 16
  • 21
KSletmoe
  • 977
  • 9
  • 23
  • Nice, how can I get #include to resolve build error? – BoazGarty Mar 09 '15 at 07:29
  • @david What is the error? The header can't be found? If so, make sure you have a recent enough version of libtiff – KSletmoe Mar 12 '15 at 16:15
  • There could be some problems with this solution, check this: http://maptools-org.996276.n3.nabble.com/In-memory-tiffs-td13305.html – FarK Sep 10 '18 at 11:56
  • How can it be that in 2012 the newest version of `libtiff` was 4.0.2, and [now in 2020](http://www.libtiff.org/) it's 3.6.1? – Rodrigo Jan 28 '20 at 13:02
  • @Rodrigo it's not; the official website is http://simplesystems.org/libtiff , which states the latest stable release as of today is v4.1.0 – KSletmoe Jan 29 '20 at 00:23
  • FYI, doesn't work if you try to write multiple images to a single TIFF. TIFFWriteDirectory needs to be able to both read from and write to the file. The writing implementation of TIFFStreamOpen doesn't support read operations, so any TIFFWriteDirectory after the first one fail. – Stefan Dragnev Feb 10 '21 at 13:49
  • In modern TIFF, `TIFFClientOpen` vs `TIFFStreamOpen` , I don't see the point. One is deprecated? – Sandburg Nov 15 '22 at 11:28
  • LibTIFF is really sub-documented. And no official examples. Very hard to catch up. – Sandburg Nov 15 '22 at 14:49
  • Is there an example of how to put a byte array (file in memory) into `istringstream`? I tried `istringstream s((char *)inputBuffer, istringstream::in || istringstream::binary);` but get `Cannot read TIFF header.` (Same data works ok if loaded directly from file with TiffOpen) – Red Riding Hood Dec 24 '22 at 12:23
  • @RedRidingHood `std::istringstream s(std::string(inputBuffer, size));` – VLL Aug 30 '23 at 11:01
7

You should create your own read/write/etc. functions and pass them to TIFFClientOpen (not TIFFOpen) function when creating your TIFF.

Example:

TIFF* tif = TIFFClientOpen(
    "Memory", "w", (thandle_t)something_you_will_use_later,
    tiff_Read, tiff_Write, tiff_Seek, tiff_Close, tiff_Size,
    tiff_Map, tiff_Unmap);

And you should also implement following functions (st passed to these functions is the something_you_will_use_later passed to TIFFClientOpen :

tsize_t tiff_Read(thandle_t st,tdata_t buffer,tsize_t size)
{
    ...
};

tsize_t tiff_Write(thandle_t st,tdata_t buffer,tsize_t size)
{
    ...
};

int tiff_Close(thandle_t)
{
    return 0;
};

toff_t tiff_Seek(thandle_t st,toff_t pos, int whence)
{
    if (pos == 0xFFFFFFFF)
       return 0xFFFFFFFF;
    ...
};

toff_t tiff_Size(thandle_t st)
{
    ...
};

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

void tiff_Unmap(thandle_t, tdata_t, toff_t)
{
    return;
};
Bobrovsky
  • 13,789
  • 19
  • 80
  • 130
  • Sorry that I'm asking again, but I don't understand how to get those functions to work. I'm trying to read from a string and put the result to a string. Could you give a sample code to do this? This would be great! – Van Coding Jan 18 '11 at 16:16
5

What I'm using...:

#define MALLOC(ptr,type,number,action) {\
if (((ptr) = (type*) malloc ((number)*sizeof(type))) == NULL) {\
    (void) fprintf (stderr, "[%s: #%04d] ERROR : malloc of %lu bytes failed !\n", __FILE__, __LINE__, number*sizeof(type));\
    perror ("Operating system message");\
    action;}}

#define REALLOC(ptr,type,number,action) {\
if (((ptr) = (type*) realloc ((ptr), (number)*sizeof(type))) == NULL) {\
    (void) fprintf (stderr, "[%s: #%04d] ERROR : realloc of %lu bytes failed!\n", __FILE__, __LINE__, number*sizeof(type));\
    perror ("Operating system message");\
    action;}}

#define FREE(ptr) { if (ptr != NULL) free (ptr); ptr = NULL; }


extern "C" {

    typedef struct _memtiff {
        unsigned char *data;
        tsize_t size;
        tsize_t incsiz;
        tsize_t flen;
        toff_t fptr;
    } MEMTIFF;

    static MEMTIFF *memTiffOpen(tsize_t incsiz = 10240, tsize_t initsiz = 10240)
    {
        MEMTIFF *memtif;
        MALLOC(memtif, MEMTIFF, 1, exit(-1));
        memtif->incsiz = incsiz;
        if (initsiz == 0) initsiz = incsiz;
        MALLOC(memtif->data, unsigned char, initsiz, exit(-1));
        memtif->size = initsiz;
        memtif->flen = 0;
        memtif->fptr = 0;
        return memtif;
    }
    /*===========================================================================*/

    static tsize_t memTiffReadProc(thandle_t handle, tdata_t buf, tsize_t size)
    {
        MEMTIFF *memtif = (MEMTIFF *) handle;
        tsize_t n;
        if (((tsize_t) memtif->fptr + size) <= memtif->flen) {
            n = size;
        }
        else {
            n = memtif->flen - memtif->fptr;
        }
        memcpy(buf, memtif->data + memtif->fptr, n);
        memtif->fptr += n;

        return n;
    }
    /*===========================================================================*/

    static tsize_t memTiffWriteProc(thandle_t handle, tdata_t buf, tsize_t size)
    {
        MEMTIFF *memtif = (MEMTIFF *) handle;
        if (((tsize_t) memtif->fptr + size) > memtif->size) {
            memtif->data = (unsigned char *) realloc(memtif->data, memtif->fptr + memtif->incsiz + size);
            memtif->size = memtif->fptr + memtif->incsiz + size;
        }
        memcpy (memtif->data + memtif->fptr, buf, size);
        memtif->fptr += size;
        if (memtif->fptr > memtif->flen) memtif->flen = memtif->fptr;

        return size;
    }
    /*===========================================================================*/

    static toff_t memTiffSeekProc(thandle_t handle, toff_t off, int whence)
    {
        MEMTIFF *memtif = (MEMTIFF *) handle;
        switch (whence) {
            case SEEK_SET: {
                if ((tsize_t) off > memtif->size) {
                    memtif->data = (unsigned char *) realloc(memtif->data, memtif->size + memtif->incsiz + off);
                    memtif->size = memtif->size + memtif->incsiz + off;
                }
                memtif->fptr = off;
                break;
            }
            case SEEK_CUR: {
                if ((tsize_t)(memtif->fptr + off) > memtif->size) {
                    memtif->data = (unsigned char *) realloc(memtif->data, memtif->fptr + memtif->incsiz + off);
                    memtif->size = memtif->fptr + memtif->incsiz + off;
                }
                memtif->fptr += off;
                break;
            }
            case SEEK_END: {
                if ((tsize_t) (memtif->size + off) > memtif->size) {
                    memtif->data = (unsigned char *) realloc(memtif->data, memtif->size + memtif->incsiz + off);
                    memtif->size = memtif->size + memtif->incsiz + off;
                }
                memtif->fptr = memtif->size + off;
                break;
            }
        }
        if (memtif->fptr > memtif->flen) memtif->flen = memtif->fptr;
        return memtif->fptr;
    }
    /*===========================================================================*/

    static int memTiffCloseProc(thandle_t handle)
    {
        MEMTIFF *memtif = (MEMTIFF *) handle;
        memtif->fptr = 0;
        return 0;
    }
    /*===========================================================================*/


    static toff_t memTiffSizeProc(thandle_t handle)
    {
        MEMTIFF *memtif = (MEMTIFF *) handle;
        return memtif->flen;
    }
    /*===========================================================================*/


    static int memTiffMapProc(thandle_t handle, tdata_t* base, toff_t* psize)
    {
        MEMTIFF *memtif = (MEMTIFF *) handle;
        *base = memtif->data;
        *psize = memtif->flen;
        return (1);
    }
    /*===========================================================================*/

    static void memTiffUnmapProc(thandle_t handle, tdata_t base, toff_t size)
    {
        return;
    }
    /*===========================================================================*/

    static void memTiffFree(MEMTIFF *memtif)
    {
        FREE(memtif->data);
        FREE(memtif);
        return;
    }
    /*===========================================================================*/

}

And then:

if ((filepath == "-") || (filepath == "HTTP")) {
    memtif = memTiffOpen();
    tif = TIFFClientOpen("MEMTIFF", "wb", (thandle_t) memtif,
        memTiffReadProc,
        memTiffWriteProc,
        memTiffSeekProc,
        memTiffCloseProc,
        memTiffSizeProc,
        memTiffMapProc,
        memTiffUnmapProc
    );
}
else {
    if ((tif = TIFFOpen (filepath.c_str(), "wb")) == NULL) {
        if (memtif != NULL) memTiffFree(memtif);
        string msg = "TIFFopen of \"" + filepath + "\" failed!";
        throw SipiError(__file__, __LINE__, msg);
    }
}

In order to use the in-memry buffer:

    if (filepath == "-") {
        size_t n = 0;
        while (n < memtif->flen) {
            n += fwrite (&(memtif->data[n]), 1, memtif->flen - n > 10240 ? 10240 : memtif->flen - n, stdout);
        }
        fflush(stdout);
        memTiffFree(memtif);
    }
0

I'm experiencing the same problem: saving TIFF as a temporary file and rereading it works, while reading the same file in memory and trying to save or create cv::Mat fails. The reason is that reading file normally goes through _tiffReadProc that consumes 8 bytes ("II*\0\b\0\0\0" in my case) and returns them as a valid header, while reading from memory goes through _tiffisReadProc that reads only 3 bytes and fails with invalid header. Don't know how to make it work. Populating std::istringstream is easy: std::istringstream input_TIFF_stream(mem); where "mem" is a char array.

  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Apr 04 '23 at 23:01
0

Another user posted that TIFFStreamOpen does not write data correctly, and I can confirm that. Only the header is written. Reading works correctly. Tested version 4.5.1 with C++ Builder.

I implemented writing to std::string by using TIFFClientOpen, which might be easier to understand than the code in another answer:

namespace WriteImpl {

struct client_data {
    std::string *buffer;
    size_t position;
};

tsize_t writeproc(thandle_t fd, tdata_t buf, tsize_t size)
{
    auto data = reinterpret_cast<client_data*>(fd);
    const size_t finalSize = data->position + size;
    if (data->buffer->size() < finalSize) {
        data->buffer->resize(finalSize);
    }
    auto srcIt = reinterpret_cast<const uint8_t*>(buf);
    auto dstIt = data->buffer->begin() + data->position;
    std::copy(srcIt, srcIt + size, dstIt);
    return size;
}

toff_t seekproc(thandle_t fd, toff_t off, int whence)
{
    auto data = reinterpret_cast<client_data*>(fd);
    switch (whence) {
    case SEEK_SET:
        data->position = static_cast<size_t>(off);
        break;
    case SEEK_CUR:
        data->position = data->position + static_cast<size_t>(off);
        break;
    case SEEK_END:
        // Since toff_t is unsigned, we cannot seek backwards from the end
        if (off == 0) {
            data->position = data->buffer->size();
        }
        else {
            return static_cast<toff_t>(-1);
        }
        break;
    }

    const size_t minSize = data->position;
    if (data->buffer->size() < minSize) {
        data->buffer->resize(minSize);
    }
    return data->position;
}

toff_t sizeproc(thandle_t fd)
{
    auto data = reinterpret_cast<client_data*>(fd);
    return data->buffer->size();
}

tsize_t readproc(thandle_t, tdata_t, tsize_t) { return 0; }
int closeproc(thandle_t) { return 0; }
int mapproc(thandle_t, tdata_t*, toff_t*) { return 0; }
void unmapproc(thandle_t, tdata_t, toff_t) {}

} // namespace WriteImpl

std::string write_stream(/* data */)
{
    using namespace WriteImpl;

    std::string buffer;
    auto clientData = std::make_unique<client_data>(client_data{&buffer, 0});

    const std::string filename = "Memory-mapped file";
    auto tiff_raw = TIFFClientOpen(filename.c_str(), "wm",
        reinterpret_cast<thandle_t>(clientData.get()),
        readproc, writeproc, seekproc, closeproc, sizeproc,
        mapproc, unmapproc);
    if (!tiff_raw) {
        throw std::runtime_error("Failed");
    }
    std::shared_ptr<TIFF> tiff(tiff_raw, &TIFFClose);

    // Write the data here
    // ...

    return buffer;
}
VLL
  • 9,634
  • 1
  • 29
  • 54