int dt_imageio_tiff_read_profile(const char *filename, uint8_t **out) { TIFF *tiff = NULL; uint32_t profile_len = 0; uint8_t *profile = NULL; if(!(filename && *filename && out)) return 0; #ifdef _WIN32 wchar_t *wfilename = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL); tiff = TIFFOpenW(wfilename, "rb"); g_free(wfilename); #else tiff = TIFFOpen(filename, "rb"); #endif if(tiff == NULL) return 0; if(TIFFGetField(tiff, TIFFTAG_ICCPROFILE, &profile_len, &profile)) { *out = (uint8_t *)malloc(profile_len); memcpy(*out, profile, profile_len); } else profile_len = 0; TIFFClose(tiff); return profile_len; }
TIFF * tiff_open (GFile *file, const gchar *mode, GError **error) { gchar *filename = g_file_get_path (file); TIFFSetWarningHandler (tiff_warning); TIFFSetErrorHandler (tiff_error); #ifdef G_OS_WIN32 gunichar2 *utf16_filename = g_utf8_to_utf16 (filename, -1, NULL, NULL, error); if (utf16_filename) { TIFF *tif = TIFFOpenW (utf16_filename, mode); g_free (utf16_filename); return tif; } return NULL; #else return TIFFOpen (filename, mode); #endif }
static TIFF * TiffOpen(Path path, const char *mode) { #ifdef _UNICODE return TIFFOpenW(path.c_str(), mode); #else return TIFFOpen(path.c_str(), mode); #endif }
/** * The constructor. * * Opens the TIFF using TIFFOpen(). * * @param filename the filename to open. * @param mode the file open mode. */ Impl(const boost::filesystem::path& filename, const std::string& mode): tiff(), directoryCount(0) { Sentry sentry; #ifdef _MSC_VER tiff = TIFFOpenW(filename.wstring().c_str(), mode.c_str()); #else tiff = TIFFOpen(filename.string().c_str(), mode.c_str()); #endif if (!tiff) sentry.error(); }
/* Open TIFF for input. */ TIFF * vips__tiff_openin( const char *path ) { /* No mmap --- no performance advantage with libtiff, and it burns up * our VM if the tiff file is large. */ const char *mode = "rm"; TIFF *tif; #ifdef DEBUG printf( "vips__tiff_openin( \"%s\" )\n", path ); #endif /*DEBUG*/ /* Need the utf-16 version on Windows. */ #ifdef OS_WIN32 { GError *error = NULL; wchar_t *path16; if( !(path16 = (wchar_t *) g_utf8_to_utf16( path, -1, NULL, NULL, &error )) ) { vips_g_error( &error ); return( NULL ); } tif = TIFFOpenW( path16, mode ); g_free( path16 ); } #else /*!OS_WIN32*/ tif = TIFFOpen( path, mode ); #endif /*OS_WIN32*/ if( !tif ) { vips_error( "tiff", _( "unable to open \"%s\" for input" ), path ); return( NULL ); } return( tif ); }
/* Open TIFF for output. */ TIFF * vips__tiff_openout( const char *path, gboolean bigtiff ) { TIFF *tif; const char *mode = bigtiff ? "w8" : "w"; #ifdef DEBUG printf( "vips__tiff_openout( \"%s\", \"%s\" )\n", path, mode ); #endif /*DEBUG*/ /* Need the utf-16 version on Windows. */ #ifdef OS_WIN32 { GError *error = NULL; wchar_t *path16; if( !(path16 = (wchar_t *) g_utf8_to_utf16( path, -1, NULL, NULL, &error )) ) { vips_g_error( &error ); return( NULL ); } tif = TIFFOpenW( path16, mode ); g_free( path16 ); } #else /*!OS_WIN32*/ tif = TIFFOpen( path, mode ); #endif /*OS_WIN32*/ if( !tif ) { vips_error( "tiff", _( "unable to open \"%s\" for output" ), path ); return( NULL ); } return( tif ); }
bool TIFFOutput::open (const std::string &name, const ImageSpec &userspec, OpenMode mode) { if (mode == AppendMIPLevel) { error ("%s does not support MIP levels", format_name()); return false; } close (); // Close any already-opened file m_spec = userspec; // Stash the spec // Check for things this format doesn't support if (m_spec.width < 1 || m_spec.height < 1) { error ("Image resolution must be at least 1x1, you asked for %d x %d", m_spec.width, m_spec.height); return false; } if (m_spec.depth < 1) m_spec.depth = 1; // Open the file #ifdef _WIN32 std::wstring wname = Strutil::utf8_to_utf16 (name); m_tif = TIFFOpenW (wname.c_str(), mode == AppendSubimage ? "a" : "w"); #else m_tif = TIFFOpen (name.c_str(), mode == AppendSubimage ? "a" : "w"); #endif if (! m_tif) { error ("Can't open \"%s\" for output.", name.c_str()); return false; } TIFFSetField (m_tif, TIFFTAG_XPOSITION, (float)m_spec.x); TIFFSetField (m_tif, TIFFTAG_YPOSITION, (float)m_spec.y); TIFFSetField (m_tif, TIFFTAG_IMAGEWIDTH, m_spec.width); TIFFSetField (m_tif, TIFFTAG_IMAGELENGTH, m_spec.height); if ((m_spec.full_width != 0 || m_spec.full_height != 0) && (m_spec.full_width != m_spec.width || m_spec.full_height != m_spec.height)) { TIFFSetField (m_tif, TIFFTAG_PIXAR_IMAGEFULLWIDTH, m_spec.full_width); TIFFSetField (m_tif, TIFFTAG_PIXAR_IMAGEFULLLENGTH, m_spec.full_height); } if (m_spec.tile_width) { TIFFSetField (m_tif, TIFFTAG_TILEWIDTH, m_spec.tile_width); TIFFSetField (m_tif, TIFFTAG_TILELENGTH, m_spec.tile_height); } else { // Scanline images must set rowsperstrip TIFFSetField (m_tif, TIFFTAG_ROWSPERSTRIP, 32); } TIFFSetField (m_tif, TIFFTAG_SAMPLESPERPIXEL, m_spec.nchannels); int orientation = m_spec.get_int_attribute("Orientation", 1); TIFFSetField (m_tif, TIFFTAG_ORIENTATION, orientation); int bps, sampformat; switch (m_spec.format.basetype) { case TypeDesc::INT8: bps = 8; sampformat = SAMPLEFORMAT_INT; break; case TypeDesc::UINT8: bps = 8; sampformat = SAMPLEFORMAT_UINT; break; case TypeDesc::INT16: bps = 16; sampformat = SAMPLEFORMAT_INT; break; case TypeDesc::UINT16: bps = 16; sampformat = SAMPLEFORMAT_UINT; break; case TypeDesc::HALF: // Silently change requests for unsupported 'half' to 'float' m_spec.set_format (TypeDesc::FLOAT); case TypeDesc::FLOAT: bps = 32; sampformat = SAMPLEFORMAT_IEEEFP; break; case TypeDesc::DOUBLE: bps = 64; sampformat = SAMPLEFORMAT_IEEEFP; break; default: // Everything else, including UNKNOWN -- default to 8 bit bps = 8; sampformat = SAMPLEFORMAT_UINT; m_spec.set_format (TypeDesc::UINT8); break; } TIFFSetField (m_tif, TIFFTAG_BITSPERSAMPLE, bps); TIFFSetField (m_tif, TIFFTAG_SAMPLEFORMAT, sampformat); int photo = (m_spec.nchannels > 1 ? PHOTOMETRIC_RGB : PHOTOMETRIC_MINISBLACK); TIFFSetField (m_tif, TIFFTAG_PHOTOMETRIC, photo); // ExtraSamples tag if (m_spec.nchannels > 3) { bool unass = m_spec.get_int_attribute("oiio:UnassociatedAlpha", 0); short e = m_spec.nchannels-3; std::vector<unsigned short> extra (e); for (int c = 0; c < e; ++c) { if (m_spec.alpha_channel == (c+3)) extra[c] = unass ? EXTRASAMPLE_UNASSALPHA : EXTRASAMPLE_ASSOCALPHA; else extra[c] = EXTRASAMPLE_UNSPECIFIED; } TIFFSetField (m_tif, TIFFTAG_EXTRASAMPLES, e, &extra[0]); } // Default to LZW compression if no request came with the user spec if (! m_spec.find_attribute("compression")) m_spec.attribute ("compression", "lzw"); ImageIOParameter *param; const char *str = NULL; // Did the user request separate planar configuration? m_planarconfig = PLANARCONFIG_CONTIG; if ((param = m_spec.find_attribute("planarconfig", TypeDesc::STRING)) || (param = m_spec.find_attribute("tiff:planarconfig", TypeDesc::STRING))) { str = *(char **)param->data(); if (str && Strutil::iequals (str, "separate")) { m_planarconfig = PLANARCONFIG_SEPARATE; if (! m_spec.tile_width) { // I can only seem to make separate planarconfig work when // rowsperstrip is 1. TIFFSetField (m_tif, TIFFTAG_ROWSPERSTRIP, 1); } } } TIFFSetField (m_tif, TIFFTAG_PLANARCONFIG, m_planarconfig); // Automatically set date field if the client didn't supply it. if (! m_spec.find_attribute("DateTime")) { time_t now; time (&now); struct tm mytm; Sysutil::get_local_time (&now, &mytm); std::string date = Strutil::format ("%4d:%02d:%02d %2d:%02d:%02d", mytm.tm_year+1900, mytm.tm_mon+1, mytm.tm_mday, mytm.tm_hour, mytm.tm_min, mytm.tm_sec); m_spec.attribute ("DateTime", date); } if (Strutil::iequals (m_spec.get_string_attribute ("oiio:ColorSpace"), "sRGB")) m_spec.attribute ("Exif:ColorSpace", 1); // Deal with all other params for (size_t p = 0; p < m_spec.extra_attribs.size(); ++p) put_parameter (m_spec.extra_attribs[p].name().string(), m_spec.extra_attribs[p].type(), m_spec.extra_attribs[p].data()); std::vector<char> iptc; encode_iptc_iim (m_spec, iptc); if (iptc.size()) { iptc.resize ((iptc.size()+3) & (0xffff-3)); // round up TIFFSetField (m_tif, TIFFTAG_RICHTIFFIPTC, iptc.size()/4, &iptc[0]); } std::string xmp = encode_xmp (m_spec, true); if (! xmp.empty()) TIFFSetField (m_tif, TIFFTAG_XMLPACKET, xmp.size(), xmp.c_str()); TIFFCheckpointDirectory (m_tif); // Ensure the header is written early m_checkpointTimer.start(); // Initialize the to the fileopen time m_checkpointItems = 0; // Number of tiles or scanlines we've written return true; }
int imb_savetiff(ImBuf *ibuf, const char *name, int flags) { TIFF *image = NULL; uint16 samplesperpixel, bitspersample; size_t npixels; unsigned char *pixels = NULL; unsigned char *from = NULL, *to = NULL; unsigned short *pixels16 = NULL, *to16 = NULL; float *fromf = NULL; float xres, yres; int x, y, from_i, to_i, i; /* check for a valid number of bytes per pixel. Like the PNG writer, * the TIFF writer supports 1, 3 or 4 bytes per pixel, corresponding * to gray, RGB, RGBA respectively. */ samplesperpixel = (uint16)((ibuf->planes + 7) >> 3); if ((samplesperpixel > 4) || (samplesperpixel == 2)) { fprintf(stderr, "imb_savetiff: unsupported number of bytes per " "pixel: %d\n", samplesperpixel); return (0); } if ((ibuf->ftype & TIF_16BIT) && ibuf->rect_float) bitspersample = 16; else bitspersample = 8; /* open TIFF file for writing */ if (flags & IB_mem) { /* bork at the creation of a TIFF in memory */ fprintf(stderr, "imb_savetiff: creation of in-memory TIFF files is " "not yet supported.\n"); return (0); } else { /* create image as a file */ #ifdef WIN32 wchar_t *wname = alloc_utf16_from_8(name, 0); image = TIFFOpenW(wname, "w"); free(wname); #else image = TIFFOpen(name, "w"); #endif } if (image == NULL) { fprintf(stderr, "imb_savetiff: could not open TIFF for writing.\n"); return (0); } /* allocate array for pixel data */ npixels = ibuf->x * ibuf->y; if (bitspersample == 16) pixels16 = (unsigned short *)_TIFFmalloc(npixels * samplesperpixel * sizeof(unsigned short)); else pixels = (unsigned char *)_TIFFmalloc(npixels * samplesperpixel * sizeof(unsigned char)); if (pixels == NULL && pixels16 == NULL) { fprintf(stderr, "imb_savetiff: could not allocate pixels array.\n"); TIFFClose(image); return (0); } /* setup pointers */ if (bitspersample == 16) { fromf = ibuf->rect_float; to16 = pixels16; } else { from = (unsigned char *)ibuf->rect; to = pixels; } /* setup samples per pixel */ TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, bitspersample); TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel); if (samplesperpixel == 4) { unsigned short extraSampleTypes[1]; if (bitspersample == 16) extraSampleTypes[0] = EXTRASAMPLE_ASSOCALPHA; else extraSampleTypes[0] = EXTRASAMPLE_UNASSALPHA; /* RGBA images */ TIFFSetField(image, TIFFTAG_EXTRASAMPLES, 1, extraSampleTypes); TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); } else if (samplesperpixel == 3) { /* RGB images */ TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); } else if (samplesperpixel == 1) { /* grayscale images, 1 channel */ TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); } /* copy pixel data. While copying, we flip the image vertically. */ for (x = 0; x < ibuf->x; x++) { for (y = 0; y < ibuf->y; y++) { from_i = 4 * (y * ibuf->x + x); to_i = samplesperpixel * ((ibuf->y - y - 1) * ibuf->x + x); if (pixels16) { /* convert from float source */ float rgb[4]; if (ibuf->float_colorspace) { /* float buffer was managed already, no need in color space conversion */ copy_v3_v3(rgb, &fromf[from_i]); } else { /* standard linear-to-srgb conversion if float buffer wasn't managed */ linearrgb_to_srgb_v3_v3(rgb, &fromf[from_i]); } rgb[3] = fromf[from_i + 3]; for (i = 0; i < samplesperpixel; i++, to_i++) to16[to_i] = FTOUSHORT(rgb[i]); } else { for (i = 0; i < samplesperpixel; i++, to_i++, from_i++) to[to_i] = from[from_i]; } } } /* write the actual TIFF file */ TIFFSetField(image, TIFFTAG_IMAGEWIDTH, ibuf->x); TIFFSetField(image, TIFFTAG_IMAGELENGTH, ibuf->y); TIFFSetField(image, TIFFTAG_ROWSPERSTRIP, ibuf->y); TIFFSetField(image, TIFFTAG_COMPRESSION, COMPRESSION_DEFLATE); TIFFSetField(image, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB); TIFFSetField(image, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); if (ibuf->ppm[0] > 0.0 && ibuf->ppm[1] > 0.0) { xres = (float)(ibuf->ppm[0] * 0.0254); yres = (float)(ibuf->ppm[1] * 0.0254); } else { xres = yres = IMB_DPI_DEFAULT; } TIFFSetField(image, TIFFTAG_XRESOLUTION, xres); TIFFSetField(image, TIFFTAG_YRESOLUTION, yres); TIFFSetField(image, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH); if (TIFFWriteEncodedStrip(image, 0, (bitspersample == 16) ? (unsigned char *)pixels16 : pixels, ibuf->x * ibuf->y * samplesperpixel * bitspersample / 8) == -1) { fprintf(stderr, "imb_savetiff: Could not write encoded TIFF.\n"); TIFFClose(image); if (pixels) _TIFFfree(pixels); if (pixels16) _TIFFfree(pixels16); return (1); } /* close the TIFF file */ TIFFClose(image); if (pixels) _TIFFfree(pixels); if (pixels16) _TIFFfree(pixels16); return (1); }
void TIFFInput::readspec (bool read_meta) { uint32 width = 0, height = 0, depth = 0; unsigned short nchans = 1; TIFFGetField (m_tif, TIFFTAG_IMAGEWIDTH, &width); TIFFGetField (m_tif, TIFFTAG_IMAGELENGTH, &height); TIFFGetFieldDefaulted (m_tif, TIFFTAG_IMAGEDEPTH, &depth); TIFFGetFieldDefaulted (m_tif, TIFFTAG_SAMPLESPERPIXEL, &nchans); if (read_meta) { // clear the whole m_spec and start fresh m_spec = ImageSpec ((int)width, (int)height, (int)nchans); } else { // assume m_spec is valid, except for things that might differ // between MIP levels m_spec.width = (int)width; m_spec.height = (int)height; m_spec.depth = (int)depth; m_spec.full_x = 0; m_spec.full_y = 0; m_spec.full_z = 0; m_spec.full_width = (int)width; m_spec.full_height = (int)height; m_spec.full_depth = (int)depth; m_spec.nchannels = (int)nchans; } float x = 0, y = 0; TIFFGetField (m_tif, TIFFTAG_XPOSITION, &x); TIFFGetField (m_tif, TIFFTAG_YPOSITION, &y); m_spec.x = (int)x; m_spec.y = (int)y; m_spec.z = 0; // FIXME? - TIFF spec describes the positions as in resolutionunit. // What happens if this is not unitless pixels? Are we interpreting // it all wrong? if (TIFFGetField (m_tif, TIFFTAG_PIXAR_IMAGEFULLWIDTH, &width) == 1 && width > 0) m_spec.full_width = width; if (TIFFGetField (m_tif, TIFFTAG_PIXAR_IMAGEFULLLENGTH, &height) == 1 && height > 0) m_spec.full_height = height; if (TIFFIsTiled (m_tif)) { TIFFGetField (m_tif, TIFFTAG_TILEWIDTH, &m_spec.tile_width); TIFFGetField (m_tif, TIFFTAG_TILELENGTH, &m_spec.tile_height); TIFFGetFieldDefaulted (m_tif, TIFFTAG_TILEDEPTH, &m_spec.tile_depth); } else { m_spec.tile_width = 0; m_spec.tile_height = 0; m_spec.tile_depth = 0; } m_bitspersample = 8; TIFFGetField (m_tif, TIFFTAG_BITSPERSAMPLE, &m_bitspersample); m_spec.attribute ("oiio:BitsPerSample", (int)m_bitspersample); unsigned short sampleformat = SAMPLEFORMAT_UINT; TIFFGetFieldDefaulted (m_tif, TIFFTAG_SAMPLEFORMAT, &sampleformat); switch (m_bitspersample) { case 1: case 2: case 4: case 6: // Make 1, 2, 4, 6 bpp look like byte images case 8: if (sampleformat == SAMPLEFORMAT_UINT) m_spec.set_format (TypeDesc::UINT8); else if (sampleformat == SAMPLEFORMAT_INT) m_spec.set_format (TypeDesc::INT8); else m_spec.set_format (TypeDesc::UINT8); // punt break; case 10: case 12: case 14: // Make 10, 12, 14 bpp look like 16 bit images case 16: if (sampleformat == SAMPLEFORMAT_UINT) m_spec.set_format (TypeDesc::UINT16); else if (sampleformat == SAMPLEFORMAT_INT) m_spec.set_format (TypeDesc::INT16); else if (sampleformat == SAMPLEFORMAT_IEEEFP) m_spec.set_format (TypeDesc::HALF); // not to spec, but why not? else m_spec.set_format (TypeDesc::UNKNOWN); break; case 32: if (sampleformat == SAMPLEFORMAT_IEEEFP) m_spec.set_format (TypeDesc::FLOAT); else if (sampleformat == SAMPLEFORMAT_UINT) m_spec.set_format (TypeDesc::UINT32); else if (sampleformat == SAMPLEFORMAT_INT) m_spec.set_format (TypeDesc::INT32); else m_spec.set_format (TypeDesc::UNKNOWN); break; case 64: if (sampleformat == SAMPLEFORMAT_IEEEFP) m_spec.set_format (TypeDesc::DOUBLE); else m_spec.set_format (TypeDesc::UNKNOWN); break; default: m_spec.set_format (TypeDesc::UNKNOWN); break; } // If we've been instructed to skip reading metadata, because it is // guaranteed to be identical to what we already have in m_spec, // skip everything following. if (! read_meta) return; // Use the table for all the obvious things that can be mindlessly // shoved into the image spec. for (int i = 0; tiff_tag_table[i].name; ++i) find_tag (tiff_tag_table[i].tifftag, tiff_tag_table[i].tifftype, tiff_tag_table[i].name); // Now we need to get fields "by hand" for anything else that is less // straightforward... m_photometric = (m_spec.nchannels == 1 ? PHOTOMETRIC_MINISBLACK : PHOTOMETRIC_RGB); TIFFGetField (m_tif, TIFFTAG_PHOTOMETRIC, &m_photometric); m_spec.attribute ("tiff:PhotometricInterpretation", (int)m_photometric); if (m_photometric == PHOTOMETRIC_PALETTE) { // Read the color map unsigned short *r = NULL, *g = NULL, *b = NULL; TIFFGetField (m_tif, TIFFTAG_COLORMAP, &r, &g, &b); ASSERT (r != NULL && g != NULL && b != NULL); m_colormap.clear (); m_colormap.insert (m_colormap.end(), r, r + (1 << m_bitspersample)); m_colormap.insert (m_colormap.end(), g, g + (1 << m_bitspersample)); m_colormap.insert (m_colormap.end(), b, b + (1 << m_bitspersample)); // Palette TIFF images are always 3 channels (to the client) m_spec.nchannels = 3; m_spec.default_channel_names (); // FIXME - what about palette + extra (alpha?) channels? Is that // allowed? And if so, ever encountered in the wild? } TIFFGetFieldDefaulted (m_tif, TIFFTAG_PLANARCONFIG, &m_planarconfig); m_separate = (m_planarconfig == PLANARCONFIG_SEPARATE && m_spec.nchannels > 1 && m_photometric != PHOTOMETRIC_PALETTE); m_spec.attribute ("tiff:PlanarConfiguration", (int)m_planarconfig); if (m_planarconfig == PLANARCONFIG_SEPARATE) m_spec.attribute ("planarconfig", "separate"); else m_spec.attribute ("planarconfig", "contig"); int compress = 0; TIFFGetFieldDefaulted (m_tif, TIFFTAG_COMPRESSION, &compress); m_spec.attribute ("tiff:Compression", compress); switch (compress) { case COMPRESSION_NONE : m_spec.attribute ("compression", "none"); break; case COMPRESSION_LZW : m_spec.attribute ("compression", "lzw"); break; case COMPRESSION_CCITTRLE : m_spec.attribute ("compression", "ccittrle"); break; case COMPRESSION_DEFLATE : case COMPRESSION_ADOBE_DEFLATE : m_spec.attribute ("compression", "zip"); break; case COMPRESSION_PACKBITS : m_spec.attribute ("compression", "packbits"); break; default: break; } int rowsperstrip = -1; if (! m_spec.tile_width) { TIFFGetField (m_tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip); if (rowsperstrip > 0) m_spec.attribute ("tiff:RowsPerStrip", rowsperstrip); } // The libtiff docs say that only uncompressed images, or those with // rowsperstrip==1, support random access to scanlines. m_no_random_access = (compress != COMPRESSION_NONE && rowsperstrip != 1); short resunit = -1; TIFFGetField (m_tif, TIFFTAG_RESOLUTIONUNIT, &resunit); switch (resunit) { case RESUNIT_NONE : m_spec.attribute ("ResolutionUnit", "none"); break; case RESUNIT_INCH : m_spec.attribute ("ResolutionUnit", "in"); break; case RESUNIT_CENTIMETER : m_spec.attribute ("ResolutionUnit", "cm"); break; } get_matrix_attribute ("worldtocamera", TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA); get_matrix_attribute ("worldtoscreen", TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN); get_int_attribute ("tiff:subfiletype", TIFFTAG_SUBFILETYPE); // FIXME -- should subfiletype be "conventionized" and used for all // plugins uniformly? // Do we care about fillorder? No, the TIFF spec says, "We // recommend that FillOrder=2 (lsb-to-msb) be used only in // special-purpose applications". So OIIO will assume msb-to-lsb // convention until somebody finds a TIFF file in the wild that // breaks this assumption. // Special names for shadow maps char *s = NULL; TIFFGetField (m_tif, TIFFTAG_PIXAR_TEXTUREFORMAT, &s); if (s) m_emulate_mipmap = true; if (s && ! strcmp (s, "Shadow")) { for (int c = 0; c < m_spec.nchannels; ++c) m_spec.channelnames[c] = "z"; } unsigned short *sampleinfo = NULL; unsigned short extrasamples = 0; TIFFGetFieldDefaulted (m_tif, TIFFTAG_EXTRASAMPLES, &extrasamples, &sampleinfo); // std::cerr << "Extra samples = " << extrasamples << "\n"; bool alpha_is_unassociated = false; // basic assumption if (extrasamples) { // If the TIFF ExtraSamples tag was specified, use that to figure // out the meaning of alpha. int colorchannels = 3; if (m_photometric == PHOTOMETRIC_MINISWHITE || m_photometric == PHOTOMETRIC_MINISBLACK || m_photometric == PHOTOMETRIC_PALETTE || m_photometric == PHOTOMETRIC_MASK) colorchannels = 1; for (int i = 0, c = colorchannels; i < extrasamples && c < m_spec.nchannels; ++i, ++c) { // std::cerr << " extra " << i << " " << sampleinfo[i] << "\n"; if (sampleinfo[i] == EXTRASAMPLE_ASSOCALPHA) { // This is the alpha channel, associated as usual m_spec.alpha_channel = c; } else if (sampleinfo[i] == EXTRASAMPLE_UNASSALPHA) { // This is the alpha channel, but color is unassociated m_spec.alpha_channel = c; alpha_is_unassociated = true; m_spec.attribute ("oiio:UnassociatedAlpha", 1); } else { DASSERT (sampleinfo[i] == EXTRASAMPLE_UNSPECIFIED); // This extra channel is not alpha at all. Undo any // assumptions we previously made about this channel. if (m_spec.alpha_channel == c) { m_spec.channelnames[c] = Strutil::format("channel%d", c); m_spec.alpha_channel = -1; } } } if (m_spec.alpha_channel >= 0) m_spec.channelnames[m_spec.alpha_channel] = "A"; } // Will we need to do alpha conversions? m_convert_alpha = (m_spec.alpha_channel >= 0 && alpha_is_unassociated && ! m_keep_unassociated_alpha); // N.B. we currently ignore the following TIFF fields: // GrayResponseCurve GrayResponseUnit // MaxSampleValue MinSampleValue // NewSubfileType SubfileType(deprecated) // Colorimetry fields // Search for an EXIF IFD in the TIFF file, and if found, rummage // around for Exif fields. #if TIFFLIB_VERSION > 20050912 /* compat with old TIFF libs - skip Exif */ int exifoffset = 0; if (TIFFGetField (m_tif, TIFFTAG_EXIFIFD, &exifoffset) && TIFFReadEXIFDirectory (m_tif, exifoffset)) { for (int i = 0; exif_tag_table[i].name; ++i) find_tag (exif_tag_table[i].tifftag, exif_tag_table[i].tifftype, exif_tag_table[i].name); // I'm not sure what state TIFFReadEXIFDirectory leaves us. // So to be safe, close and re-seek. TIFFClose (m_tif); #ifdef _WIN32 std::wstring wfilename = Filesystem::path_to_windows_native (m_filename); m_tif = TIFFOpenW (wfilename.c_str(), "rm"); #else m_tif = TIFFOpen (m_filename.c_str(), "rm"); #endif TIFFSetDirectory (m_tif, m_subimage); // A few tidbits to look for ImageIOParameter *p; if ((p = m_spec.find_attribute ("Exif:ColorSpace", TypeDesc::INT))) { // Exif spec says that anything other than 0xffff==uncalibrated // should be interpreted to be sRGB. if (*(const int *)p->data() != 0xffff) m_spec.attribute ("oiio::ColorSpace", "sRGB"); } } #endif #if TIFFLIB_VERSION >= 20051230 // Search for IPTC metadata in IIM form -- but older versions of // libtiff botch the size, so ignore it for very old libtiff. int iptcsize = 0; const void *iptcdata = NULL; if (TIFFGetField (m_tif, TIFFTAG_RICHTIFFIPTC, &iptcsize, &iptcdata)) { std::vector<uint32> iptc ((uint32 *)iptcdata, (uint32 *)iptcdata+iptcsize); if (TIFFIsByteSwapped (m_tif)) TIFFSwabArrayOfLong ((uint32*)&iptc[0], iptcsize); decode_iptc_iim (&iptc[0], iptcsize*4, m_spec); } #endif // Search for an XML packet containing XMP (IPTC, Exif, etc.) int xmlsize = 0; const void *xmldata = NULL; if (TIFFGetField (m_tif, TIFFTAG_XMLPACKET, &xmlsize, &xmldata)) { // std::cerr << "Found XML data, size " << xmlsize << "\n"; if (xmldata && xmlsize) { std::string xml ((const char *)xmldata, xmlsize); decode_xmp (xml, m_spec); } } #if 0 // Experimental -- look for photoshop data int photoshopsize = 0; const void *photoshopdata = NULL; if (TIFFGetField (m_tif, TIFFTAG_PHOTOSHOP, &photoshopsize, &photoshopdata)) { std::cerr << "Found PHOTOSHOP data, size " << photoshopsize << "\n"; if (photoshopdata && photoshopsize) { // std::string photoshop ((const char *)photoshopdata, photoshopsize); // std::cerr << "PHOTOSHOP:\n" << photoshop << "\n---\n"; } } #endif }
bool TIFFInput::seek_subimage (int subimage, int miplevel, ImageSpec &newspec) { if (subimage < 0) // Illegal return false; if (m_emulate_mipmap) { // Emulating MIPmap? Pretend one subimage, many MIP levels. if (subimage != 0) return false; subimage = miplevel; } else { // No MIPmap emulation if (miplevel != 0) return false; } if (subimage == m_subimage) { // We're already pointing to the right subimage newspec = m_spec; return true; } // If we're emulating a MIPmap, only resolution is allowed to change // between MIP levels, so if we already have a valid level in m_spec, // we don't need to re-parse metadata, it's guaranteed to be the same. bool read_meta = !(m_emulate_mipmap && m_tif && m_subimage >= 0); if (! m_tif) { // Use our own error handler to keep libtiff from spewing to stderr lock_guard lock (lasterr_mutex); TIFFSetErrorHandler (my_error_handler); TIFFSetWarningHandler (my_error_handler); } if (! m_tif) { #ifdef _WIN32 std::wstring wfilename = Filesystem::path_to_windows_native (m_filename); m_tif = TIFFOpenW (wfilename.c_str(), "rm"); #else m_tif = TIFFOpen (m_filename.c_str(), "rm"); #endif if (m_tif == NULL) { error ("Could not open file: %s", lasterr.length() ? lasterr.c_str() : m_filename.c_str()); return false; } m_subimage = 0; } m_next_scanline = 0; // next scanline we'll read if (TIFFSetDirectory (m_tif, subimage)) { m_subimage = subimage; readspec (read_meta); newspec = m_spec; if (newspec.format == TypeDesc::UNKNOWN) { error ("No support for data format of \"%s\"", m_filename.c_str()); return false; } return true; } else { error ("%s", lasterr.length() ? lasterr.c_str() : m_filename.c_str()); m_subimage = -1; return false; } }
bool TIFFOutput::open (const std::string &name, const ImageSpec &userspec, OpenMode mode) { if (mode == AppendMIPLevel) { error ("%s does not support MIP levels", format_name()); return false; } close (); // Close any already-opened file m_spec = userspec; // Stash the spec // Check for things this format doesn't support if (m_spec.width < 1 || m_spec.height < 1) { error ("Image resolution must be at least 1x1, you asked for %d x %d", m_spec.width, m_spec.height); return false; } if (m_spec.tile_width) { if (m_spec.tile_width % 16 != 0 || m_spec.tile_height % 16 != 0 || m_spec.tile_height == 0) { error("Tile size must be a multiple of 16, you asked for %d x %d", m_spec.tile_width, m_spec.tile_height); return false; } } if (m_spec.depth < 1) m_spec.depth = 1; // Open the file #ifdef _WIN32 std::wstring wname = Strutil::utf8_to_utf16 (name); m_tif = TIFFOpenW (wname.c_str(), mode == AppendSubimage ? "a" : "w"); #else m_tif = TIFFOpen (name.c_str(), mode == AppendSubimage ? "a" : "w"); #endif if (! m_tif) { error ("Can't open \"%s\" for output.", name.c_str()); return false; } // N.B. Clamp position at 0... TIFF is internally incapable of having // negative origin. TIFFSetField (m_tif, TIFFTAG_XPOSITION, (float)std::max (0, m_spec.x)); TIFFSetField (m_tif, TIFFTAG_YPOSITION, (float)std::max (0, m_spec.y)); TIFFSetField (m_tif, TIFFTAG_IMAGEWIDTH, m_spec.width); TIFFSetField (m_tif, TIFFTAG_IMAGELENGTH, m_spec.height); if ((m_spec.full_width != 0 || m_spec.full_height != 0) && (m_spec.full_width != m_spec.width || m_spec.full_height != m_spec.height)) { TIFFSetField (m_tif, TIFFTAG_PIXAR_IMAGEFULLWIDTH, m_spec.full_width); TIFFSetField (m_tif, TIFFTAG_PIXAR_IMAGEFULLLENGTH, m_spec.full_height); } if (m_spec.tile_width) { TIFFSetField (m_tif, TIFFTAG_TILEWIDTH, m_spec.tile_width); TIFFSetField (m_tif, TIFFTAG_TILELENGTH, m_spec.tile_height); } else { // Scanline images must set rowsperstrip TIFFSetField (m_tif, TIFFTAG_ROWSPERSTRIP, 32); } TIFFSetField (m_tif, TIFFTAG_SAMPLESPERPIXEL, m_spec.nchannels); int orientation = m_spec.get_int_attribute("Orientation", 1); TIFFSetField (m_tif, TIFFTAG_ORIENTATION, orientation); m_bitspersample = m_spec.get_int_attribute ("oiio:BitsPerSample"); int sampformat; switch (m_spec.format.basetype) { case TypeDesc::INT8: m_bitspersample = 8; sampformat = SAMPLEFORMAT_INT; break; case TypeDesc::UINT8: if (m_bitspersample != 2 && m_bitspersample != 4) m_bitspersample = 8; sampformat = SAMPLEFORMAT_UINT; break; case TypeDesc::INT16: m_bitspersample = 16; sampformat = SAMPLEFORMAT_INT; break; case TypeDesc::UINT16: if (m_bitspersample != 10 && m_bitspersample != 12) m_bitspersample = 16; sampformat = SAMPLEFORMAT_UINT; break; case TypeDesc::INT32: m_bitspersample = 32; sampformat = SAMPLEFORMAT_INT; break; case TypeDesc::UINT32: m_bitspersample = 32; sampformat = SAMPLEFORMAT_UINT; break; case TypeDesc::HALF: #if 0 // Adobe extension, see http://chriscox.org/TIFFTN3d1.pdf // Unfortunately, Nuke 9.0, and probably many other apps we care // about, cannot read 16 bit float TIFFs correctly. Revisit this // again in future releases. (comment added Feb 2015) m_bitspersample = 16; sampformat = SAMPLEFORMAT_IEEEFP; break; #else // Silently change requests for unsupported 'half' to 'float' m_spec.set_format (TypeDesc::FLOAT); #endif case TypeDesc::FLOAT: m_bitspersample = 32; sampformat = SAMPLEFORMAT_IEEEFP; break; case TypeDesc::DOUBLE: m_bitspersample = 64; sampformat = SAMPLEFORMAT_IEEEFP; break; default: // Everything else, including UNKNOWN -- default to 8 bit m_bitspersample = 8; sampformat = SAMPLEFORMAT_UINT; m_spec.set_format (TypeDesc::UINT8); break; } TIFFSetField (m_tif, TIFFTAG_BITSPERSAMPLE, m_bitspersample); TIFFSetField (m_tif, TIFFTAG_SAMPLEFORMAT, sampformat); m_photometric = (m_spec.nchannels > 1 ? PHOTOMETRIC_RGB : PHOTOMETRIC_MINISBLACK); string_view comp = m_spec.get_string_attribute("Compression", "zip"); if (Strutil::iequals (comp, "jpeg") && (m_spec.format != TypeDesc::UINT8 || m_spec.nchannels != 3)) { comp = "zip"; // can't use JPEG for anything but 3xUINT8 } m_compression = tiff_compression_code (comp); TIFFSetField (m_tif, TIFFTAG_COMPRESSION, m_compression); // Use predictor when using compression if (m_compression == COMPRESSION_LZW || m_compression == COMPRESSION_ADOBE_DEFLATE) { if (m_spec.format == TypeDesc::FLOAT || m_spec.format == TypeDesc::DOUBLE || m_spec.format == TypeDesc::HALF) { TIFFSetField (m_tif, TIFFTAG_PREDICTOR, PREDICTOR_FLOATINGPOINT); // N.B. Very old versions of libtiff did not support this // predictor. It's possible that certain apps can't read // floating point TIFFs with this set. But since it's been // documented since 2005, let's take our chances. Comment // out the above line if this is problematic. } else if (m_bitspersample == 8 || m_bitspersample == 16) { // predictors not supported for unusual bit depths (e.g. 10) TIFFSetField (m_tif, TIFFTAG_PREDICTOR, PREDICTOR_HORIZONTAL); } } else if (m_compression == COMPRESSION_JPEG) { TIFFSetField (m_tif, TIFFTAG_JPEGQUALITY, m_spec.get_int_attribute("CompressionQuality", 95)); TIFFSetField (m_tif, TIFFTAG_ROWSPERSTRIP, 64); m_spec.attribute ("tiff:RowsPerStrip", 64); if (m_photometric == PHOTOMETRIC_RGB) { // Compression works so much better when we ask the library to // auto-convert RGB to YCbCr. TIFFSetField (m_tif, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB); m_photometric = PHOTOMETRIC_YCBCR; } } m_outputchans = m_spec.nchannels; if (m_photometric == PHOTOMETRIC_RGB) { // There are a few ways in which we allow allow the user to specify // translation to different photometric types. string_view photo = m_spec.get_string_attribute("tiff:ColorSpace"); if (Strutil::iequals (photo, "CMYK")) { // CMYK: force to 4 channel output, either uint8 or uint16 m_photometric = PHOTOMETRIC_SEPARATED; m_outputchans = 4; TIFFSetField (m_tif, TIFFTAG_SAMPLESPERPIXEL, m_outputchans); if (m_spec.format != TypeDesc::UINT8 || m_spec.format != TypeDesc::UINT16) { m_spec.format = TypeDesc::UINT8; m_bitspersample = 8; TIFFSetField (m_tif, TIFFTAG_BITSPERSAMPLE, m_bitspersample); TIFFSetField (m_tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); } } } TIFFSetField (m_tif, TIFFTAG_PHOTOMETRIC, m_photometric); // ExtraSamples tag if (m_spec.nchannels > 3 && m_photometric != PHOTOMETRIC_SEPARATED) { bool unass = m_spec.get_int_attribute("oiio:UnassociatedAlpha", 0); short e = m_spec.nchannels-3; std::vector<unsigned short> extra (e); for (int c = 0; c < e; ++c) { if (m_spec.alpha_channel == (c+3)) extra[c] = unass ? EXTRASAMPLE_UNASSALPHA : EXTRASAMPLE_ASSOCALPHA; else extra[c] = EXTRASAMPLE_UNSPECIFIED; } TIFFSetField (m_tif, TIFFTAG_EXTRASAMPLES, e, &extra[0]); } ImageIOParameter *param; const char *str = NULL; // Did the user request separate planar configuration? m_planarconfig = PLANARCONFIG_CONTIG; if ((param = m_spec.find_attribute("planarconfig", TypeDesc::STRING)) || (param = m_spec.find_attribute("tiff:planarconfig", TypeDesc::STRING))) { str = *(char **)param->data(); if (str && Strutil::iequals (str, "separate")) m_planarconfig = PLANARCONFIG_SEPARATE; } // Can't deal with the headache of separate image planes when using // bit packing, or CMYK. Just punt by forcing contig in those cases. if (m_bitspersample != spec().format.size()*8 || m_photometric == PHOTOMETRIC_SEPARATED) m_planarconfig = PLANARCONFIG_CONTIG; if (m_planarconfig == PLANARCONFIG_SEPARATE) { if (! m_spec.tile_width) { // I can only seem to make separate planarconfig work when // rowsperstrip is 1. TIFFSetField (m_tif, TIFFTAG_ROWSPERSTRIP, 1); } } TIFFSetField (m_tif, TIFFTAG_PLANARCONFIG, m_planarconfig); // Automatically set date field if the client didn't supply it. if (! m_spec.find_attribute("DateTime")) { time_t now; time (&now); struct tm mytm; Sysutil::get_local_time (&now, &mytm); std::string date = Strutil::format ("%4d:%02d:%02d %2d:%02d:%02d", mytm.tm_year+1900, mytm.tm_mon+1, mytm.tm_mday, mytm.tm_hour, mytm.tm_min, mytm.tm_sec); m_spec.attribute ("DateTime", date); } // Write ICC profile, if we have anything const ImageIOParameter* icc_profile_parameter = m_spec.find_attribute(ICC_PROFILE_ATTR); if (icc_profile_parameter != NULL) { unsigned char *icc_profile = (unsigned char*)icc_profile_parameter->data(); uint32 length = icc_profile_parameter->type().size(); if (icc_profile && length) TIFFSetField (m_tif, TIFFTAG_ICCPROFILE, length, icc_profile); } if (Strutil::iequals (m_spec.get_string_attribute ("oiio:ColorSpace"), "sRGB")) m_spec.attribute ("Exif:ColorSpace", 1); // Deal with missing XResolution or YResolution, or a PixelAspectRatio // that contradicts them. float X_density = m_spec.get_float_attribute ("XResolution", 1.0f); float Y_density = m_spec.get_float_attribute ("YResolution", 1.0f); float aspect = m_spec.get_float_attribute ("PixelAspectRatio", 1.0f); if (X_density < 1.0f || Y_density < 1.0f || aspect*X_density != Y_density) { if (X_density < 1.0f || Y_density < 1.0f) { X_density = Y_density = 1.0f; m_spec.attribute ("ResolutionUnit", "none"); } m_spec.attribute ("XResolution", X_density); m_spec.attribute ("YResolution", X_density * aspect); } // Deal with all other params for (size_t p = 0; p < m_spec.extra_attribs.size(); ++p) put_parameter (m_spec.extra_attribs[p].name().string(), m_spec.extra_attribs[p].type(), m_spec.extra_attribs[p].data()); std::vector<char> iptc; encode_iptc_iim (m_spec, iptc); if (iptc.size()) { iptc.resize ((iptc.size()+3) & (0xffff-3)); // round up TIFFSetField (m_tif, TIFFTAG_RICHTIFFIPTC, iptc.size()/4, &iptc[0]); } std::string xmp = encode_xmp (m_spec, true); if (! xmp.empty()) TIFFSetField (m_tif, TIFFTAG_XMLPACKET, xmp.size(), xmp.c_str()); TIFFCheckpointDirectory (m_tif); // Ensure the header is written early m_checkpointTimer.start(); // Initialize the to the fileopen time m_checkpointItems = 0; // Number of tiles or scanlines we've written m_dither = (m_spec.format == TypeDesc::UINT8) ? m_spec.get_int_attribute ("oiio:dither", 0) : 0; return true; }
int write_image(dt_imageio_module_data_t *d_tmp, const char *filename, const void *in_void, dt_colorspaces_color_profile_type_t over_type, const char *over_filename, void *exif, int exif_len, int imgid, int num, int total, dt_dev_pixelpipe_t *pipe) { const dt_imageio_tiff_t *d = (dt_imageio_tiff_t *)d_tmp; uint8_t *profile = NULL; uint32_t profile_len = 0; TIFF *tif = NULL; void *rowdata = NULL; int rc = 1; // default to error if(imgid > 0) { cmsHPROFILE out_profile = dt_colorspaces_get_output_profile(imgid, over_type, over_filename)->profile; cmsSaveProfileToMem(out_profile, 0, &profile_len); if(profile_len > 0) { profile = malloc(profile_len); if(!profile) { rc = 1; goto exit; } cmsSaveProfileToMem(out_profile, profile, &profile_len); } } // Create little endian tiff image #ifdef _WIN32 wchar_t *wfilename = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL); tif = TIFFOpenW(wfilename, "wl"); g_free(wfilename); #else tif = TIFFOpen(filename, "wl"); #endif if(!tif) { rc = 1; goto exit; } // http://partners.adobe.com/public/developer/en/tiff/TIFFphotoshop.pdf (dated 2002) // "A proprietary ZIP/Flate compression code (0x80b2) has been used by some" // "software vendors. This code should be considered obsolete. We recommend" // "that TIFF implementations recognize and read the obsolete code but only" // "write the official compression code (0x0008)." // http://www.awaresystems.be/imaging/tiff/tifftags/compression.html // http://www.awaresystems.be/imaging/tiff/tifftags/predictor.html if(d->compress == 1) { TIFFSetField(tif, TIFFTAG_COMPRESSION, (uint16_t)COMPRESSION_ADOBE_DEFLATE); TIFFSetField(tif, TIFFTAG_PREDICTOR, (uint16_t)PREDICTOR_NONE); TIFFSetField(tif, TIFFTAG_ZIPQUALITY, (uint16_t)d->compresslevel); } else if(d->compress == 2) { TIFFSetField(tif, TIFFTAG_COMPRESSION, (uint16_t)COMPRESSION_ADOBE_DEFLATE); TIFFSetField(tif, TIFFTAG_PREDICTOR, (uint16_t)PREDICTOR_HORIZONTAL); TIFFSetField(tif, TIFFTAG_ZIPQUALITY, (uint16_t)d->compresslevel); } else if(d->compress == 3) { TIFFSetField(tif, TIFFTAG_COMPRESSION, (uint16_t)COMPRESSION_ADOBE_DEFLATE); if(d->bpp == 32) TIFFSetField(tif, TIFFTAG_PREDICTOR, (uint16_t)PREDICTOR_FLOATINGPOINT); else TIFFSetField(tif, TIFFTAG_PREDICTOR, (uint16_t)PREDICTOR_HORIZONTAL); TIFFSetField(tif, TIFFTAG_ZIPQUALITY, (uint16_t)d->compresslevel); } else // (d->compress == 0) { TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); } TIFFSetField(tif, TIFFTAG_FILLORDER, (uint16_t)FILLORDER_MSB2LSB); if(profile != NULL) { TIFFSetField(tif, TIFFTAG_ICCPROFILE, (uint32_t)profile_len, profile); } TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, (uint16_t)3); TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, (uint16_t)d->bpp); TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, (uint16_t)(d->bpp == 32 ? SAMPLEFORMAT_IEEEFP : SAMPLEFORMAT_UINT)); TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (uint32_t)d->global.width); TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (uint32_t)d->global.height); TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, (uint16_t)PHOTOMETRIC_RGB); TIFFSetField(tif, TIFFTAG_PLANARCONFIG, (uint16_t)PLANARCONFIG_CONTIG); TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, (uint32_t)1); TIFFSetField(tif, TIFFTAG_ORIENTATION, (uint16_t)ORIENTATION_TOPLEFT); int resolution = dt_conf_get_int("metadata/resolution"); if(resolution > 0) { TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)resolution); TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)resolution); TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, (uint16_t)RESUNIT_INCH); } const size_t rowsize = (d->global.width * 3) * d->bpp / 8; if((rowdata = malloc(rowsize)) == NULL) { rc = 1; goto exit; } if(d->bpp == 32) { for(int y = 0; y < d->global.height; y++) { float *in = (float *)in_void + (size_t)4 * y * d->global.width; float *out = (float *)rowdata; for(int x = 0; x < d->global.width; x++, in += 4, out += 3) { memcpy(out, in, 3 * sizeof(float)); } if(TIFFWriteScanline(tif, rowdata, y, 0) == -1) { rc = 1; goto exit; } } } else if(d->bpp == 16) { for(int y = 0; y < d->global.height; y++) { uint16_t *in = (uint16_t *)in_void + (size_t)4 * y * d->global.width; uint16_t *out = (uint16_t *)rowdata; for(int x = 0; x < d->global.width; x++, in += 4, out += 3) { memcpy(out, in, 3 * sizeof(uint16_t)); } if(TIFFWriteScanline(tif, rowdata, y, 0) == -1) { rc = 1; goto exit; } } } else { for(int y = 0; y < d->global.height; y++) { uint8_t *in = (uint8_t *)in_void + (size_t)4 * y * d->global.width; uint8_t *out = (uint8_t *)rowdata; for(int x = 0; x < d->global.width; x++, in += 4, out += 3) { memcpy(out, in, 3 * sizeof(uint8_t)); } if(TIFFWriteScanline(tif, rowdata, y, 0) == -1) { rc = 1; goto exit; } } } // success rc = 0; exit: // close the file before adding exif data if(tif) { TIFFClose(tif); tif = NULL; } if(!rc && exif) { rc = dt_exif_write_blob(exif, exif_len, filename, d->compress > 0); // Until we get symbolic error status codes, if rc is 1, return 0 rc = (rc == 1) ? 0 : 1; } free(profile); profile = NULL; free(rowdata); rowdata = NULL; return rc; }
void Tiff::OpenForWriting(const char *file) { Close(); m_tiff = reinterpret_cast<void *>(TIFFOpenW(wstring(file),"w")); }
dt_imageio_retval_t dt_imageio_open_tiff(dt_image_t *img, const char *filename, dt_mipmap_buffer_t *mbuf) { // doing this once would be enough, but our imageio reading code is // compiled into dt's core and doesn't have an init routine. TIFFSetWarningHandler(_warning_handler); TIFFSetErrorHandler(_error_handler); const char *ext = filename + strlen(filename); while(*ext != '.' && ext > filename) ext--; if(strncmp(ext, ".tif", 4) && strncmp(ext, ".TIF", 4) && strncmp(ext, ".tiff", 5) && strncmp(ext, ".TIFF", 5)) return DT_IMAGEIO_FILE_CORRUPTED; if(!img->exif_inited) (void)dt_exif_read(img, filename); tiff_t t; uint16_t config; t.image = img; #ifdef _WIN32 wchar_t *wfilename = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL); t.tiff = TIFFOpenW(wfilename, "rb"); g_free(wfilename); #else t.tiff = TIFFOpen(filename, "rb"); #endif if(t.tiff == NULL) return DT_IMAGEIO_FILE_CORRUPTED; TIFFGetField(t.tiff, TIFFTAG_IMAGEWIDTH, &t.width); TIFFGetField(t.tiff, TIFFTAG_IMAGELENGTH, &t.height); TIFFGetField(t.tiff, TIFFTAG_BITSPERSAMPLE, &t.bpp); TIFFGetField(t.tiff, TIFFTAG_SAMPLESPERPIXEL, &t.spp); TIFFGetFieldDefaulted(t.tiff, TIFFTAG_SAMPLEFORMAT, &t.sampleformat); TIFFGetField(t.tiff, TIFFTAG_PLANARCONFIG, &config); if(TIFFRasterScanlineSize(t.tiff) != TIFFScanlineSize(t.tiff)) return DT_IMAGEIO_FILE_CORRUPTED; t.scanlinesize = TIFFScanlineSize(t.tiff); dt_print(DT_DEBUG_CAMERA_SUPPORT, "[tiff_open] %dx%d %dbpp, %d samples per pixel.\n", t.width, t.height, t.bpp, t.spp); // we only support 8/16 and 32 bits per pixel formats. if(t.bpp != 8 && t.bpp != 16 && t.bpp != 32) { TIFFClose(t.tiff); return DT_IMAGEIO_FILE_CORRUPTED; } /* we only support 1,3 or 4 samples per pixel */ if(t.spp != 1 && t.spp != 3 && t.spp != 4) { TIFFClose(t.tiff); return DT_IMAGEIO_FILE_CORRUPTED; } /* initialize cached image buffer */ t.image->width = t.width; t.image->height = t.height; t.image->buf_dsc.channels = 4; t.image->buf_dsc.datatype = TYPE_FLOAT; t.mipbuf = (float *)dt_mipmap_cache_alloc(mbuf, t.image); if(!t.mipbuf) { fprintf(stderr, "[tiff_open] error: could not alloc full buffer for image `%s'\n", t.image->filename); TIFFClose(t.tiff); return DT_IMAGEIO_CACHE_FULL; } /* don't depend on planar config if spp == 1 */ if(t.spp > 1 && config != PLANARCONFIG_CONTIG) { fprintf(stderr, "[tiff_open] error: planar config other than contig is not supported.\n"); TIFFClose(t.tiff); return DT_IMAGEIO_FILE_CORRUPTED; } if((t.buf = _TIFFmalloc(t.scanlinesize)) == NULL) { TIFFClose(t.tiff); return DT_IMAGEIO_CACHE_FULL; } int ok = 1; if(t.bpp == 8 && t.sampleformat == SAMPLEFORMAT_UINT && config == PLANARCONFIG_CONTIG) ok = _read_planar_8(&t); else if(t.bpp == 16 && t.sampleformat == SAMPLEFORMAT_UINT && config == PLANARCONFIG_CONTIG) ok = _read_planar_16(&t); else if(t.bpp == 32 && t.sampleformat == SAMPLEFORMAT_IEEEFP && config == PLANARCONFIG_CONTIG) ok = _read_planar_f(&t); else { fprintf(stderr, "[tiff_open] error: Not a supported tiff image format."); ok = 0; } _TIFFfree(t.buf); TIFFClose(t.tiff); return (ok == 1 ? DT_IMAGEIO_OK : DT_IMAGEIO_FILE_CORRUPTED); }