
Thread
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
- the order I'd prefer:
- IFD0, IFD1, thumbnail image, primary image.
- Some software finds the thumbnail in IFD1, e.g. "exiftool - htmldump thumb.tif"
- *
- Paul Heckbert, 2012/11/15, 2012/12/7
- */
#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 Checkpoint, you'll get errors like this on later SetDirectory:
- MissingRequired: TIFF directory is missing required "StripOffsets" field.
* if you leave off the Write, you'll get warnings like
* _TIFFVGetField: thumb.tif: Invalid tag "Predictor" (not
supported by codec).
- plus a failed assertion on SetDirectory.
- The trick of following Checkpoint by Write was suggested by Olivier Paquet.
- Adding the Write causes the new directory to be cleared.
- */
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