Esempio n. 1
0
int
TIFFFlush(TIFF* tif)
{
    if( tif->tif_mode == O_RDONLY )
        return 1;

    if (!TIFFFlushData(tif))
        return (0);

    /* In update (r+) mode we try to detect the case where
       only the strip/tile map has been altered, and we try to
       rewrite only that portion of the directory without
       making any other changes */

    if( (tif->tif_flags & TIFF_DIRTYSTRIP)
        && !(tif->tif_flags & TIFF_DIRTYDIRECT)
        && tif->tif_mode == O_RDWR )
    {
        uint64  *offsets=NULL, *sizes=NULL;

        if( TIFFIsTiled(tif) )
        {
            if( TIFFGetField( tif, TIFFTAG_TILEOFFSETS, &offsets )
                && TIFFGetField( tif, TIFFTAG_TILEBYTECOUNTS, &sizes )
                && _TIFFRewriteField( tif, TIFFTAG_TILEOFFSETS, TIFF_LONG8,
                                      tif->tif_dir.td_nstrips, offsets )
                && _TIFFRewriteField( tif, TIFFTAG_TILEBYTECOUNTS, TIFF_LONG8,
                                      tif->tif_dir.td_nstrips, sizes ) )
            {
                tif->tif_flags &= ~TIFF_DIRTYSTRIP;
                tif->tif_flags &= ~TIFF_BEENWRITING;
                return 1;
            }
        }
        else
        {
            if( TIFFGetField( tif, TIFFTAG_STRIPOFFSETS, &offsets )
                && TIFFGetField( tif, TIFFTAG_STRIPBYTECOUNTS, &sizes )
                && _TIFFRewriteField( tif, TIFFTAG_STRIPOFFSETS, TIFF_LONG8,
                                      tif->tif_dir.td_nstrips, offsets )
                && _TIFFRewriteField( tif, TIFFTAG_STRIPBYTECOUNTS, TIFF_LONG8,
                                      tif->tif_dir.td_nstrips, sizes ) )
            {
                tif->tif_flags &= ~TIFF_DIRTYSTRIP;
                tif->tif_flags &= ~TIFF_BEENWRITING;
                return 1;
            }
        }
    }

    if ((tif->tif_flags & (TIFF_DIRTYDIRECT|TIFF_DIRTYSTRIP))
        && !TIFFRewriteDirectory(tif))
        return (0);

    return (1);
}
Esempio n. 2
0
File: tools.c Progetto: 3dptv/3dptv
int write_tiff (const char path[256], unsigned char *data, int nx, int ny)
{
	TIFF *tif;
	unsigned char *data1;						/* pixel data */
	unsigned char *buf, *buf1, *begin, *end;	/* scanline buffer */
	int  y;

	/* open tiff file */
	tif = TIFFOpen(path, "w");

	if (tif == NULL)
	{
		fprintf (stderr, "Error opening TIFF file: %s\n", path);
		return(-1);
	}

	/* set tiff fields */
	TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, nx);
	TIFFSetField(tif, TIFFTAG_IMAGELENGTH, ny);
	TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
	TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
	TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
	TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
	TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
	TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);

	/* memory for scanline buffer */
	buf = (unsigned char*) malloc (TIFFScanlineSize(tif));
	if (buf == NULL)
	{
		fprintf (stderr, "Save TIFF roi: Cannot allocate memory ");
		fprintf (stderr, "for scanline buffer.\n");
		return(-1);
	}

	/* read the required scanlines and copy the required portion */
	data1 = data;
	begin = buf;
	end = begin + nx;

	for (y = 0; y < ny; y++)
	{
		for (buf1 = begin; buf1 < end; buf1++)
			*buf1 = *data1++;
		TIFFWriteScanline (tif, buf, y, 0);
	}

	/* release scanline buffer */
	free (buf);

	/* flush data and close file */
	TIFFFlushData(tif);
	TIFFClose(tif);
	return (1);
}
Esempio n. 3
0
static gint
save_contiguous(GeglOperation *operation,
                GeglBuffer    *input,
                const GeglRectangle *result,
                const Babl *format)
{
  GeglProperties *o = GEGL_PROPERTIES(operation);
  Priv *p = (Priv*) o->user_data;
  gint bytes_per_pixel, bytes_per_row;
  gint tile_width = result->width;
  gint tile_height = result->height;
  guchar *buffer;
  gint x, y;

  g_return_val_if_fail(p->tiff != NULL, -1);

  bytes_per_pixel = babl_format_get_bytes_per_pixel(format);
  bytes_per_row = bytes_per_pixel * tile_width;

  buffer = g_try_new(guchar, bytes_per_row * tile_height);

  g_assert(buffer != NULL);

  for (y = result->y; y < result->y + tile_height; y += tile_height)
    {
      for (x = result->x; x < result->x + tile_width; x += tile_width)
        {
          GeglRectangle tile = { x, y, tile_width, tile_height };
          gint row;

          gegl_buffer_get(input, &tile, 1.0, format, buffer,
                          GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);

          for (row = y; row < y + tile_height; row++)
            {
              guchar *tile_row = buffer + (bytes_per_row * (row - y));
              gint written;

              written = TIFFWriteScanline(p->tiff, tile_row, row, 0);

              if (!written)
                {
                  g_critical("failed a scanline write on row %d", row);
                  continue;
                }
            }
        }
    }

  TIFFFlushData(p->tiff);

  g_free(buffer);
  return 0;
}
Esempio n. 4
0
int
TIFFFlush(TIFF* tif)
{

	if (tif->tif_mode != O_RDONLY) {
		if (!TIFFFlushData(tif))
			return (0);
		if ((tif->tif_flags & TIFF_DIRTYDIRECT) &&
		    !TIFFWriteDirectory(tif))
			return (0);
	}
	return (1);
}
Esempio n. 5
0
static void
destroyTiffGenerator(WriteMethod const writeMethod,
                     TIFF *      const tifP,
                     int         const ofd) {

    TIFFFlushData(tifP);

    if (writeMethod == TMPFILE)
        copyBufferToStdout(ofd);

    /* If we copied the buffer above, the buffer file is already closed
       (copyBufferToStdout closes it), TIFFClose appears to tolerate that -
       all it does is a close() and doesn't mind that it fails.
    */
    TIFFClose(tifP);
}
Esempio n. 6
0
int
TIFFWriteScanline(TIFF* tif, void* buf, uint32 row, uint16 sample)
{
	static const char module[] = "TIFFWriteScanline";
	register TIFFDirectory *td;
	int status, imagegrew = 0;
	uint32 strip;

	if (!WRITECHECKSTRIPS(tif, module))
		return (-1);
	/*
	 * Handle delayed allocation of data buffer.  This
	 * permits it to be sized more intelligently (using
	 * directory information).
	 */
	if (!BUFFERCHECK(tif))
		return (-1);
        tif->tif_flags |= TIFF_BUF4WRITE; /* not strictly sure this is right*/

	td = &tif->tif_dir;
	/*
	 * Extend image length if needed
	 * (but only for PlanarConfig=1).
	 */
	if (row >= td->td_imagelength) {	/* extend image */
		if (td->td_planarconfig == PLANARCONFIG_SEPARATE) {
			TIFFErrorExt(tif->tif_clientdata, module,
			    "Can not change \"ImageLength\" when using separate planes");
			return (-1);
		}
		td->td_imagelength = row+1;
		imagegrew = 1;
	}
	/*
	 * Calculate strip and check for crossings.
	 */
	if (td->td_planarconfig == PLANARCONFIG_SEPARATE) {
		if (sample >= td->td_samplesperpixel) {
			TIFFErrorExt(tif->tif_clientdata, module,
			    "%lu: Sample out of range, max %lu",
			    (unsigned long) sample, (unsigned long) td->td_samplesperpixel);
			return (-1);
		}
		strip = sample*td->td_stripsperimage + row/td->td_rowsperstrip;
	} else
		strip = row / td->td_rowsperstrip;
	/*
	 * Check strip array to make sure there's space. We don't support
	 * dynamically growing files that have data organized in separate
	 * bitplanes because it's too painful.  In that case we require that
	 * the imagelength be set properly before the first write (so that the
	 * strips array will be fully allocated above).
	 */
	if (strip >= td->td_nstrips && !TIFFGrowStrips(tif, 1, module))
		return (-1);
	if (strip != tif->tif_curstrip) {
		/*
		 * Changing strips -- flush any data present.
		 */
		if (!TIFFFlushData(tif))
			return (-1);
		tif->tif_curstrip = strip;
		/*
		 * Watch out for a growing image.  The value of strips/image
		 * will initially be 1 (since it can't be deduced until the
		 * imagelength is known).
		 */
		if (strip >= td->td_stripsperimage && imagegrew)
			td->td_stripsperimage =
			    TIFFhowmany_32(td->td_imagelength,td->td_rowsperstrip);
                if (td->td_stripsperimage == 0) {
                        TIFFErrorExt(tif->tif_clientdata, module, "Zero strips per image");
                        return (-1);
                }
		tif->tif_row =
		    (strip % td->td_stripsperimage) * td->td_rowsperstrip;
		if ((tif->tif_flags & TIFF_CODERSETUP) == 0) {
			if (!(*tif->tif_setupencode)(tif))
				return (-1);
			tif->tif_flags |= TIFF_CODERSETUP;
		}
        
		tif->tif_rawcc = 0;
		tif->tif_rawcp = tif->tif_rawdata;

		if( td->td_stripbytecount[strip] > 0 )
		{
			/* if we are writing over existing tiles, zero length */
			td->td_stripbytecount[strip] = 0;

			/* this forces TIFFAppendToStrip() to do a seek */
			tif->tif_curoff = 0;
		}

		if (!(*tif->tif_preencode)(tif, sample))
			return (-1);
		tif->tif_flags |= TIFF_POSTENCODE;
	}
	/*
	 * Ensure the write is either sequential or at the
	 * beginning of a strip (or that we can randomly
	 * access the data -- i.e. no encoding).
	 */
	if (row != tif->tif_row) {
		if (row < tif->tif_row) {
			/*
			 * Moving backwards within the same strip:
			 * backup to the start and then decode
			 * forward (below).
			 */
			tif->tif_row = (strip % td->td_stripsperimage) *
			    td->td_rowsperstrip;
			tif->tif_rawcp = tif->tif_rawdata;
		}
		/*
		 * Seek forward to the desired row.
		 */
		if (!(*tif->tif_seek)(tif, row - tif->tif_row))
			return (-1);
		tif->tif_row = row;
	}

	/* swab if needed - note that source buffer will be altered */
	tif->tif_postdecode( tif, (uint8*) buf, tif->tif_scanlinesize );

	status = (*tif->tif_encoderow)(tif, (uint8*) buf,
	    tif->tif_scanlinesize, sample);

        /* we are now poised at the beginning of the next row */
	tif->tif_row = row + 1;
	return (status);
}
Esempio n. 7
0
static void
destroyTiffGenerator(TIFF * const tifP) {

    TIFFFlushData(tifP);
    TIFFClose(tifP);
}
Esempio n. 8
0
/* This function will write an entire directory to the disk, and return the
   offset value indicating where in the file it wrote the beginning of the
   directory structure. This is NOT the same as the offset value before
   calling this function, because some of the fields may have caused various
   data items to be written out BEFORE writing the directory structure.

   This code was basically written by ripping of the TIFFWriteDirectory() 
   code and generalizing it, using RPS's TIFFWritePliIfd() code for
   inspiration.  My original goal was to make this code general enough that
   the original TIFFWriteDirectory() could be rewritten to just call this
   function with the appropriate field and field-accessing arguments.

   However, now I realize that there's a lot of code that gets executed for
   the main, standard TIFF directories that does not apply to special
   private subdirectories, so such a reimplementation for the sake of
   eliminating redundant or duplicate code is probably not possible,
   unless we also pass in a Main flag to indiciate which type of handling
   to do, which would be kind of a hack. I've marked those places where I
   changed or ripped out code which would have to be re-inserted to
   generalize this function. If it can be done in a clean and graceful way,
   it would be a great way to generalize the TIFF library. Otherwise, I'll
   just leave this code here where it duplicates but remains on top of and
   hopefully mostly independent of the main TIFF library.

   The caller will probably want to free the sub directory structure after
   returning from this call, since otherwise once written out, the user
   is likely to forget about it and leave data lying around.
*/
toff_t
TIFFWritePrivateDataSubDirectory(TIFF* tif,
				 uint32 pdir_fieldsset[], int pdir_fields_last,
				 TIFFFieldInfo *field_info,
				 int (*getFieldFn)(TIFF *tif, ttag_t tag, ...))
{
	uint16 dircount;
	uint32 diroff, nextdiroff;
	ttag_t tag;
	uint32 nfields;
	tsize_t dirsize;
	char* data;
	TIFFDirEntry* dir;
	u_long b, *fields, fields_size;
	toff_t directory_offset;
	TIFFFieldInfo* fip;

	/*
	 * Deleted out all of the encoder flushing and such code from here -
	 * not necessary for subdirectories.
	 */

	/* Finish writing out any image data. */
	TIFFFlushData(tif);

	/*
	 * Size the directory so that we can calculate
	 * offsets for the data items that aren't kept
	 * in-place in each field.
	 */
	nfields = 0;
	for (b = 0; b <= pdir_fields_last; b++)
		if (FieldSet(pdir_fieldsset, b))
			/* Deleted code to make size of first 4 tags 2
			   instead of 1. */
			nfields += 1;
	dirsize = nfields * sizeof (TIFFDirEntry);
	data = (char*) _TIFFmalloc(dirsize);
	if (data == NULL) {
		TIFFError(tif->tif_name,
		    "Cannot write private subdirectory, out of space");
		return (0);
	}
	/*
	 * Place directory in data section of the file. If there isn't one
	 * yet, place it at the end of the file. The directory is treated as
	 * data, so we don't link it into the directory structure at all.
	 */
	if (tif->tif_dataoff == 0)
	    tif->tif_dataoff =(TIFFSeekFile(tif, (toff_t) 0, SEEK_END)+1) &~ 1;
	diroff = tif->tif_dataoff;
	tif->tif_dataoff = (toff_t)(
	    diroff + sizeof (uint16) + dirsize + sizeof (toff_t));
	if (tif->tif_dataoff & 1)
		tif->tif_dataoff++;
	(void) TIFFSeekFile(tif, tif->tif_dataoff, SEEK_SET);
	/*tif->tif_curdir++;*/
	dir = (TIFFDirEntry*) data;
	/*
	 * Setup external form of directory
	 * entries and write data items.
	 */
	/*
	 * We make a local copy of the fieldsset here so that we don't mess
	 * up the original one when we call ResetFieldBit(). But I'm not sure
	 * why the original code calls ResetFieldBit(), since we're already
	 * going through the fields in order...
	 *
	 * fields_size is the number of uint32's we will need to hold the
	 * bit-mask for all of the fields. If our highest field number is
	 * 100, then we'll need 100 / (8*4)+1 == 4 uint32's to hold the
	 * fieldset.
	 *
	 * Unlike the original code, we allocate fields dynamically based
	 * on the requested pdir_fields_last value, allowing private
	 * data subdirectories to contain more than the built-in code's limit
	 * of 95 tags in a directory.
	 */
	fields_size = pdir_fields_last / (8*sizeof(uint32)) + 1;
	fields = _TIFFmalloc(fields_size*sizeof(uint32));
	_TIFFmemcpy(fields, pdir_fieldsset, fields_size * sizeof(uint32));

	/* Deleted "write out extra samples tag" code here. */

	/* Deleted code for checking a billion little special cases for the
	 * standard TIFF tags. Should add a general mechanism for overloading
	 * write function for each field, just like Brian kept telling me!!!
	 */
	for (fip = field_info; fip->field_tag; fip++) {
		/* Deleted code to check for FIELD_IGNORE!! */
		if (/* fip->field_bit == FIELD_IGNORE || */
		    !FieldSet(fields, fip->field_bit))
			continue;
		if (!TIFFWriteNormalSubTag(tif, dir, fip, getFieldFn))
			goto bad;
		dir++;
		ResetFieldBit(fields, fip->field_bit);
	}

	/* Now we've written all of the referenced data, and are about to
	   write the main directory structure, so grab the tif_dataoff value
	   now so we can remember where we wrote the directory. */
	directory_offset = tif->tif_dataoff;

	/*
	 * Write directory.
	 */
	dircount = (uint16) nfields;
	/* Deleted code to link to the next directory - we set it to zero! */
	nextdiroff = 0;
	if (tif->tif_flags & TIFF_SWAB) {
		/*
		 * The file's byte order is opposite to the
		 * native machine architecture.  We overwrite
		 * the directory information with impunity
		 * because it'll be released below after we
		 * write it to the file.  Note that all the
		 * other tag construction routines assume that
		 * we do this byte-swapping; i.e. they only
		 * byte-swap indirect data.
		 */
		for (dir = (TIFFDirEntry*) data; dircount; dir++, dircount--) {
			TIFFSwabArrayOfShort(&dir->tdir_tag, 2);
			TIFFSwabArrayOfLong(&dir->tdir_count, 2);
		}
		dircount = (uint16) nfields;
		TIFFSwabShort(&dircount);
		TIFFSwabLong(&nextdiroff);
	}

	(void) TIFFSeekFile(tif, tif->tif_dataoff, SEEK_SET);
	if (!WriteOK(tif, &dircount, sizeof (dircount))) {
		TIFFError(tif->tif_name, "Error writing private subdirectory count");
		goto bad;
	}
	if (!WriteOK(tif, data, dirsize)) {
		TIFFError(tif->tif_name, "Error writing private subdirectory contents");
		goto bad;
	}
	if (!WriteOK(tif, &nextdiroff, sizeof (nextdiroff))) {
		TIFFError(tif->tif_name, "Error writing private subdirectory link");
		goto bad;
	}
	tif->tif_dataoff += sizeof(dircount) + dirsize + sizeof(nextdiroff);

	_TIFFfree(data);
	_TIFFfree(fields);
	tif->tif_flags &= ~TIFF_DIRTYDIRECT;

#if (0)
	/* This stuff commented out because I don't think we want it for
	   subdirectories, but I could be wrong. */
	(*tif->tif_cleanup)(tif);

	/*
	 * Reset directory-related state for subsequent
	 * directories.
	 */
	TIFFDefaultDirectory(tif);
	tif->tif_curoff = 0;
	tif->tif_row = (uint32) -1;
	tif->tif_curstrip = (tstrip_t) -1;
#endif

	return (directory_offset);
bad:
	_TIFFfree(data);
	_TIFFfree(fields);
	return (0);
}
Esempio n. 9
0
int
TIFFWriteScanline(TIFF* tif, tdata_t buf, uint32 row, tsample_t sample)
{
    static const char module[] = "TIFFWriteScanline";
    register TIFFDirectory *td;
    int status, imagegrew = 0;
    tstrip_t strip;

    if (!WRITECHECKSTRIPS(tif, module))
        return (-1);
    /*
     * Handle delayed allocation of data buffer.  This
     * permits it to be sized more intelligently (using
     * directory information).
     */
    if (!BUFFERCHECK(tif))
        return (-1);
    td = &tif->tif_dir;
    /*
     * Extend image length if needed
     * (but only for PlanarConfig=1).
     */
    if (row >= td->td_imagelength) {    /* extend image */
        if (td->td_planarconfig == PLANARCONFIG_SEPARATE) {
            TIFFError(tif->tif_name,
        "Can not change \"ImageLength\" when using separate planes");
            return (-1);
        }
        td->td_imagelength = row+1;
        imagegrew = 1;
    }
    /*
     * Calculate strip and check for crossings.
     */
    if (td->td_planarconfig == PLANARCONFIG_SEPARATE) {
        if (sample >= td->td_samplesperpixel) {
            TIFFError(tif->tif_name,
                "%d: Sample out of range, max %d",
                sample, td->td_samplesperpixel);
            return (-1);
        }
        strip = sample*td->td_stripsperimage + row/td->td_rowsperstrip;
    } else
        strip = row / td->td_rowsperstrip;
    if (strip != tif->tif_curstrip) {
        /*
         * Changing strips -- flush any data present.
         */
        if (!TIFFFlushData(tif))
            return (-1);
        tif->tif_curstrip = strip;
        /*
         * Watch out for a growing image.  The value of
         * strips/image will initially be 1 (since it
         * can't be deduced until the imagelength is known).
         */
        if (strip >= td->td_stripsperimage && imagegrew)
            td->td_stripsperimage =
                TIFFhowmany(td->td_imagelength,td->td_rowsperstrip);
        tif->tif_row =
            (strip % td->td_stripsperimage) * td->td_rowsperstrip;
        if ((tif->tif_flags & TIFF_CODERSETUP) == 0) {
            if (!(*tif->tif_setupencode)(tif))
                return (-1);
            tif->tif_flags |= TIFF_CODERSETUP;
        }
        if (!(*tif->tif_preencode)(tif, sample))
            return (-1);
        tif->tif_flags |= TIFF_POSTENCODE;
    }
    /*
     * Check strip array to make sure there's space.
     * We don't support dynamically growing files that
     * have data organized in separate bitplanes because
     * it's too painful.  In that case we require that
     * the imagelength be set properly before the first
     * write (so that the strips array will be fully
     * allocated above).
     */
    if (strip >= td->td_nstrips && !TIFFGrowStrips(tif, 1, module))
        return (-1);
    /*
     * Ensure the write is either sequential or at the
     * beginning of a strip (or that we can randomly
     * access the data -- i.e. no encoding).
     */
    if (row != tif->tif_row) {
        if (row < tif->tif_row) {
            /*
             * Moving backwards within the same strip:
             * backup to the start and then decode
             * forward (below).
             */
            tif->tif_row = (strip % td->td_stripsperimage) *
                td->td_rowsperstrip;
            tif->tif_rawcp = tif->tif_rawdata;
        }
        /*
         * Seek forward to the desired row.
         */
        if (!(*tif->tif_seek)(tif, row - tif->tif_row))
            return (-1);
        tif->tif_row = row;
    }
    status = (*tif->tif_encoderow)(tif, (tidata_t) buf,
        tif->tif_scanlinesize, sample);
    tif->tif_row++;
    return (status);
}
Esempio n. 10
0
static gboolean
_gdk_pixbuf_save_as_tiff (GdkPixbuf   *pixbuf,
			  const char  *filename,
			  char       **keys,
			  char       **values,
			  GError     **error)
{
	TIFF          *tif;
	int            cols, col, rows, row;
	glong          rowsperstrip;
	gushort        compression;
	/*gushort        extra_samples[1]; FIXME*/
	int            alpha;
	gshort         predictor;
	gshort         photometric;
	gshort         samplesperpixel;
	gshort         bitspersample;
	int            rowstride;
	guchar        *pixels, *buf, *ptr;
	int            success;
	int            horizontal_dpi = 72, vertical_dpi = 72;
	gboolean       save_resolution = FALSE;

	compression = COMPRESSION_DEFLATE;

	if (keys && *keys) {
		char **kiter = keys;
		char **viter = values;

		while (*kiter) {
			if (strcmp (*kiter, "compression") == 0) {
				if (*viter == NULL) {
					g_set_error (error,
						     GDK_PIXBUF_ERROR,
						     GDK_PIXBUF_ERROR_BAD_OPTION,
						     "Must specify a compression type");
					return FALSE;
				}

				if (strcmp (*viter, "none") == 0)
					compression = COMPRESSION_NONE;
				else if (strcmp (*viter, "pack bits") == 0)
					compression = COMPRESSION_PACKBITS;
				else if (strcmp (*viter, "lzw") == 0)
					compression = COMPRESSION_LZW;
				else if (strcmp (*viter, "deflate") == 0)
					compression = COMPRESSION_DEFLATE;
				else if (strcmp (*viter, "jpeg") == 0)
					compression = COMPRESSION_JPEG;
				else {
					g_set_error (error,
						     GDK_PIXBUF_ERROR,
						     GDK_PIXBUF_ERROR_BAD_OPTION,
						     "Unsupported compression type passed to the TIFF saver");
					return FALSE;
				}

			} else if (strcmp (*kiter, "vertical dpi") == 0) {
				char *endptr = NULL;
				vertical_dpi = strtol (*viter, &endptr, 10);
				save_resolution = TRUE;

				if (endptr == *viter) {
					g_set_error (error,
						     GDK_PIXBUF_ERROR,
						     GDK_PIXBUF_ERROR_BAD_OPTION,
						     "TIFF vertical dpi must be a value greater than 0; value '%s' could not be parsed.",
						     *viter);

					return FALSE;
				}

				if (vertical_dpi < 0) {
					g_set_error (error,
						     GDK_PIXBUF_ERROR,
						     GDK_PIXBUF_ERROR_BAD_OPTION,
						     "TIFF vertical dpi must be a value greater than 0; value '%d' is not allowed.",
						     vertical_dpi);

					return FALSE;
				}

			} else if (strcmp (*kiter, "horizontal dpi") == 0) {
				char *endptr = NULL;
				horizontal_dpi = strtol (*viter, &endptr, 10);
				save_resolution = TRUE;

				if (endptr == *viter) {
					g_set_error (error,
						     GDK_PIXBUF_ERROR,
						     GDK_PIXBUF_ERROR_BAD_OPTION,
						     "TIFF horizontal dpi must be a value greater than 0; value '%s' could not be parsed.",
						     *viter);

					return FALSE;
				}

				if (horizontal_dpi < 0) {
					g_set_error (error,
						     GDK_PIXBUF_ERROR,
						     GDK_PIXBUF_ERROR_BAD_OPTION,
						     "TIFF horizontal dpi must be a value greater than 0; value '%d' is not allowed.",
						     horizontal_dpi);

					return FALSE;
				}
			} else {
				g_warning ("Bad option name '%s' passed to the TIFF saver", *kiter);
				return FALSE;
			}

			++kiter;
			++viter;
		}
	}

	predictor    = 0;
	rowsperstrip = TILE_HEIGHT;

	tif = TIFFOpen (filename, "w");

	if (tif == NULL) {
		g_set_error (error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_FAILED,
			     "Can't write image to file '%s'",
			     filename);
		return FALSE;
	}

	cols      = gdk_pixbuf_get_width (pixbuf);
	rows      = gdk_pixbuf_get_height (pixbuf);
	alpha     = gdk_pixbuf_get_has_alpha (pixbuf);
	pixels    = gdk_pixbuf_get_pixels (pixbuf);
	rowstride = gdk_pixbuf_get_rowstride (pixbuf);

	predictor       = 2;
	bitspersample   = 8;
	photometric     = PHOTOMETRIC_RGB;

	if (alpha)
		samplesperpixel = 4;
	else
		samplesperpixel = 3;

	/* Set TIFF parameters. */

	TIFFSetField (tif, TIFFTAG_SUBFILETYPE,   0);
	TIFFSetField (tif, TIFFTAG_IMAGEWIDTH,    cols);
	TIFFSetField (tif, TIFFTAG_IMAGELENGTH,   rows);
	TIFFSetField (tif, TIFFTAG_BITSPERSAMPLE, bitspersample);
	TIFFSetField (tif, TIFFTAG_ORIENTATION,   ORIENTATION_TOPLEFT);
	TIFFSetField (tif, TIFFTAG_COMPRESSION,   compression);

	if ((compression == COMPRESSION_LZW
	     || compression == COMPRESSION_DEFLATE)
	    && (predictor != 0)) {
		TIFFSetField (tif, TIFFTAG_PREDICTOR, predictor);
	}

	/* FIXME: alpha in a TIFF ?
	if (alpha) {
		extra_samples [0] = EXTRASAMPLE_ASSOCALPHA;
		TIFFSetField (tif, TIFFTAG_EXTRASAMPLES, 1, extra_samples);
	}
	*/

	TIFFSetField (tif, TIFFTAG_PHOTOMETRIC,     photometric);
	TIFFSetField (tif, TIFFTAG_DOCUMENTNAME,    filename);
	TIFFSetField (tif, TIFFTAG_SAMPLESPERPIXEL, 3 /*samplesperpixel*/);
	TIFFSetField (tif, TIFFTAG_ROWSPERSTRIP,    rowsperstrip);
	TIFFSetField (tif, TIFFTAG_PLANARCONFIG,    PLANARCONFIG_CONTIG);

	if (save_resolution) {
		TIFFSetField (tif, TIFFTAG_XRESOLUTION, (double) horizontal_dpi);
		TIFFSetField (tif, TIFFTAG_YRESOLUTION, (double) vertical_dpi);
		TIFFSetField (tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
	}

	/* allocate a small buffer to convert image data */
	buf = g_try_malloc (cols * 3 /*samplesperpixel*/ * sizeof (guchar));
	if (! buf) {
		g_set_error (error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
			     "Couldn't allocate memory for writing TIFF file '%s'",
			     filename);
		return FALSE;
	}

	ptr = pixels;

	/* Now write the TIFF data. */
	for (row = 0; row < rows; row++) {
		/* convert scanline from ARGB to RGB packed */
		for (col = 0; col < cols; col++)
			memcpy (&(buf[col * 3]), &(ptr[col * samplesperpixel /*3*/]), 3);

		success = TIFFWriteScanline (tif, buf, row, 0) >= 0;

		if (! success) {
			g_set_error (error,
				     GDK_PIXBUF_ERROR,
				     GDK_PIXBUF_ERROR_FAILED,
				     "TIFF Failed a scanline write on row %d",
				     row);
			return FALSE;
		}

		ptr += rowstride;
	}

	TIFFFlushData (tif);
	TIFFClose (tif);

	g_free (buf);

	return TRUE;
}