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); }
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); }
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; }
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); }
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); }
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); }
static void destroyTiffGenerator(TIFF * const tifP) { TIFFFlushData(tifP); TIFFClose(tifP); }
/* 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); }
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); }
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; }