2012.11.24 00:09 "[Tiff] help writing thumbnails to TIFF file?", by Paul Heckbert

2012.12.08 04:35 "Re: [Tiff] help writing thumbnails to TIFF file?", by Paul Heckbert

Olivier: thanks very much! Although mysterious, your trick does indeed work. I like its simplicity!

Daniel: thanks. I helped talk Darwyn into interviewing at Pixar, years ago. Small world!

Here's the final version of my test program. I suggest this gets added to the contrib directory of the distribution, if others agree with it.

/* Write a test picture to a TIFF file, with thumbnail, using libtiff.

  *
  * Run "thumb" to write data in the order:
  *   primary image, IFD0, thumbnail image, IFD1.
  * Run "thumb -checkpoint" to use TIFFCheckpointDirectory to write in  

#ifdef _WIN32
   #include <windows.h>
#endif

#include <assert.h>

#include <stdlib.h>

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

/* write a picture that's a horizontal ramp in red, vertical ramp in green, and specified blue */

static void write_image(TIFF *tif, int nx, int ny, int blue) {

 uint8 *buf = _TIFFmalloc(nx*3);
 int x, y;

assert(buf);

for (y=0; y<ny; y++) {
  uint8 *p = buf;
  for (x=0; x<nx; x++) {

     *p++ = x*255/(nx-1);  /* r */
     *p++ = y*255/(ny-1);  /* g */

       *p++ = blue;

  }
  assert(TIFFWriteScanline(tif, buf, y, 0) != -1);
}
_TIFFfree(buf);

}

static void set_tags(TIFF *tif, int nx, int ny, int compression) {

printf("set_tags %d %d %d\n", nx, ny, compression);

assert(TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, nx));
assert(TIFFSetField(tif, TIFFTAG_IMAGELENGTH, ny));
assert(TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8));
assert(TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB));

   /* set strip size not too big */
   assert(TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, 1000));

assert(TIFFSetField(tif, TIFFTAG_COMPRESSION, compression)); if (compression == COMPRESSION_LZW)

   assert(TIFFSetField(tif, TIFFTAG_PREDICTOR, PREDICTOR_HORIZONTAL));
 assert(TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT));
 assert(TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG));
 assert(TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3));
}

int main(int argc, char **argv) {
 int px = 640, py /* primary image size */ ;
 int tx = 160, ty = 120; /* thumbnail image size */
 char *filename = "thumb.tif";

   int i, checkpoint = 0;
   TIFF *tif;

   for (i=1; i<argc; i++) {
     if (argv[i][0] == '-') {
       if (strcmp(argv[i], "-checkpoint") == 0)
         checkpoint = 1;
       else {
         printf("Error: unknown arg (%s)\n", argv[i]);
         printf("Usage: %s [-checkpoint] [WIDTH]\n", argv[0]);
         return 1;
       }
     } else {
       px = atoi(argv[i]);
     }
   }
   py = px*3/4;
   assert(py>0);

   tif = TIFFOpen(filename, "w");

assert(tif);

if (checkpoint) {

     printf("checkpoint\n");

  set_tags(tif, px, py, COMPRESSION_LZW);
  /* write IFD0 (preliminary version) and create an empty IFD1 */
  assert(TIFFCheckpointDirectory(tif));

  assert(TIFFWriteDirectory(tif));

     /* about the Checkpoint,Write combo above:

      *   if you leave off the Write, you'll get warnings like
      *     _TIFFVGetField: thumb.tif: Invalid tag "Predictor" (not  

supported by codec).

  set_tags(tif, tx, ty, COMPRESSION_NONE);
  /* write IFD1 (preliminary version) and create an empty IFD2 */
  assert(TIFFCheckpointDirectory(tif));

  assert(TIFFWriteDirectory(tif));

  /* set current dir to IFD1 */
  assert(TIFFSetDirectory(tif, 1));

     /* write pixels of thumbnail (has blue in upper left) */
     write_image(tif, tx, ty, 255);
     /* rewrite IFD1 */

  assert(TIFFWriteDirectory(tif));
  /* set current dir to IFD0 */
  assert(TIFFSetDirectory(tif, 0));

     /* write pixels of primary image (has black in upper left) */
     write_image(tif, px, py, 0);
   } else {
     printf("nocheckpoint\n");

  set_tags(tif, px, py, COMPRESSION_LZW);

     /* write pixels of primary image (has black in upper left) */
     write_image(tif, px, py, 0);

  /* write IFD0 */
  assert(TIFFWriteDirectory(tif));
  set_tags(tif, tx, ty, COMPRESSION_NONE);

     /* write pixels of thumbnail (has blue in upper left) */
     write_image(tif, tx, ty, 255);

}

 /* causes TIFFWriteDirectory to be called */
 TIFFClose(tif);
 return 0;
}

On 2012/11/27, at 10:06 PM, Olivier Paquet wrote:

> On Tue, Nov 27, 2012 at 9:38 AM, Olivier Paquet > <olivier.paquet@gmail.com> wrote:

>>> My current hypothesis: TIFFCheckpointDirectory works fine if you're >>> writing only one directory, but it's buggy when there are multiple

>>> directories.
>>

>> Quite possible. It works by keeping the directory in memory for >> further checkpoints or the final write. Getting it to work with

>> multiple directories requires rereading them (with TIFFSetDirectory) >> which is very different. It does look like it should work so it can't

>> be that far from actually working. I will try to find some time to >> take a closer look.

>

> After trying a few ugly hacks in libtiff, I got an apparently valid > file with both libtiff 3 and 4 by adding a TIFFWriteDirectory call

> after each of your TIFFCheckpointDirectory calls. The file looks good > in tiffinfo and tiffdump, will split with tiffsplit and opens in KDE's

> viewer (okular) as two pages of different resolution. tiffdump shows > the layout to be IFD0, IFD1, data for IFD1, data for IFD0. I don't

> know how future-proof this is but it at least appears to work for now, > in this specific case. Oh and it even passes valgrind :)

>
> Olivier