2
// Copyright (C) 1997 - 2007, Paul C. Gregory
4
// Contact: pgregory@aqsis.org
6
// This library is free software; you can redistribute it and/or
7
// modify it under the terms of the GNU General Public
8
// License as published by the Free Software Foundation; either
9
// version 2 of the License, or (at your option) any later version.
11
// This library is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
// General Public License for more details.
16
// You should have received a copy of the GNU General Public
17
// License along with this library; if not, write to the Free Software
18
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
* \brief A C++ wrapper around tiff files for the functions of interest in aqsis.
24
* \author Chris Foster
27
#include "tiffdirhandle.h"
30
#include <cstring> // for memcpy()
34
#include <aqsis/math/math.h>
35
#include <aqsis/math/matrix.h>
36
#include <aqsis/util/logging.h>
37
#include <aqsis/tex/texexception.h>
42
/// An exception for internal usage by the tiff handling code.
43
AQSIS_DECLARE_XQEXCEPTION(XqUnknownTiffFormat, XqInternal);
45
//------------------------------------------------------------------------------
46
// Helper functions and data for dealing with tiff <--> header conversions.
47
//------------------------------------------------------------------------------
50
/// String constants which describe the various texture types.
51
const char* plainTextureFormatStr = "Plain Texture";
52
const char* cubeEnvTextureFormatStr = "CubeFace Environment";
53
const char* latlongEnvTextureFormatStr = "LatLong Environment";
54
const char* shadowTextureFormatStr = "Shadow";
55
const char* occlusionTextureFormatStr = "Occlusion";
57
/// Convert from a string to an EqTextureFormat
58
EqTextureFormat texFormatFromString(const std::string& str)
60
if(str == plainTextureFormatStr)
61
return TextureFormat_Plain;
62
else if(str == cubeEnvTextureFormatStr)
63
return TextureFormat_CubeEnvironment;
64
else if(str == latlongEnvTextureFormatStr)
65
return TextureFormat_LatLongEnvironment;
66
else if(str == shadowTextureFormatStr)
67
return TextureFormat_Shadow;
68
else if(str == occlusionTextureFormatStr)
69
return TextureFormat_Occlusion;
70
return TextureFormat_Unknown;
73
/// Convert from an EqTextureFormat to a string.
74
const char* texFormatToString(EqTextureFormat format)
78
case TextureFormat_Plain:
79
return plainTextureFormatStr;
80
case TextureFormat_CubeEnvironment:
81
return cubeEnvTextureFormatStr;
82
case TextureFormat_LatLongEnvironment:
83
return latlongEnvTextureFormatStr;
84
case TextureFormat_Shadow:
85
return shadowTextureFormatStr;
86
case TextureFormat_Occlusion:
87
return occlusionTextureFormatStr;
88
case TextureFormat_Unknown:
91
assert("unhandled format type" && 0);
92
return "unknown"; // shut up compiler warning.
95
typedef std::pair<uint16, const char*> TqComprPair;
96
TqComprPair comprTypesInit[] = {
97
TqComprPair(COMPRESSION_NONE, "none"),
98
TqComprPair(COMPRESSION_LZW, "lzw"),
99
TqComprPair(COMPRESSION_JPEG, "jpeg"),
100
TqComprPair(COMPRESSION_PACKBITS, "packbits"),
101
TqComprPair(COMPRESSION_SGILOG, "log"),
102
TqComprPair(COMPRESSION_DEFLATE, "deflate"),
104
/// vector holding the TIFF compression alternatives that we want to deal with.
105
const std::vector<TqComprPair> compressionTypes( comprTypesInit,
106
comprTypesInit + sizeof(comprTypesInit)/sizeof(TqComprPair));
108
/// Get the tiff compression type from a string description.
109
uint16 tiffCompressionTagFromName(const std::string& compressionName)
111
for(std::vector<TqComprPair>::const_iterator i = compressionTypes.begin();
112
i != compressionTypes.end(); ++i)
114
if(i->second == compressionName)
117
return COMPRESSION_NONE;
120
/// Get the tiff compression type from a string description.
121
const char* tiffCompressionNameFromTag(uint16 compressionType)
123
for(std::vector<TqComprPair>::const_iterator i = compressionTypes.begin();
124
i != compressionTypes.end(); ++i)
126
if(i->first == compressionType)
132
} // unnamed namespace
134
//------------------------------------------------------------------------------
136
//------------------------------------------------------------------------------
138
CqTiffDirHandle::CqTiffDirHandle(const boost::shared_ptr<CqTiffFileHandle>& fileHandle, const tdir_t dirIdx)
139
: m_fileHandle(fileHandle)
141
fileHandle->setDirectory(dirIdx);
144
tdir_t CqTiffDirHandle::dirIndex() const
146
return m_fileHandle->m_currDir;
149
void CqTiffDirHandle::fillHeader(CqTexFileHeader& header) const
151
fillHeaderRequiredAttrs(header);
152
fillHeaderOptionalAttrs(header);
153
fillHeaderPixelLayout(header);
156
void CqTiffDirHandle::writeHeader(const CqTexFileHeader& header)
158
writeRequiredAttrs(header);
159
writeOptionalAttrs(header);
162
void CqTiffDirHandle::writeRequiredAttrs(const CqTexFileHeader& header)
165
setTiffTagValue<uint32>(TIFFTAG_IMAGEWIDTH, header.width());
166
setTiffTagValue<uint32>(TIFFTAG_IMAGELENGTH, header.height());
168
// Orientation & planar config should always be fixed.
169
setTiffTagValue<uint16>(TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
170
setTiffTagValue<uint16>(TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
172
// Pixel aspect ratio
173
// We have no meaningful resolution unit - we're only interested in pixel
174
// aspect ratio, so set the resolution unit to none.
175
setTiffTagValue<uint16>(TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE);
176
setTiffTagValue<float>(TIFFTAG_XRESOLUTION, 1.0f);
177
setTiffTagValue<float>(TIFFTAG_YRESOLUTION, header.find<Attr::PixelAspectRatio>(1));
179
// Compression-related stuff
180
writeCompressionAttrs(header);
181
// Channel-related stuff
182
writeChannelAttrs(header);
184
const SqTileInfo* tileInfo = header.findPtr<Attr::TileInfo>();
187
// Set tile dimensions if present.
188
setTiffTagValue<uint32>(TIFFTAG_TILEWIDTH, tileInfo->width);
189
setTiffTagValue<uint32>(TIFFTAG_TILELENGTH, tileInfo->height);
193
// Else write strip size - AFAICT libtiff uses the values of some other
194
// fields (compression) to choose a default, so do this last.
195
setTiffTagValue<uint32>(TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tiffPtr(), 0));
199
void CqTiffDirHandle::writeCompressionAttrs(const CqTexFileHeader& header)
201
// Set the compression type.
202
uint16 compression = tiffCompressionTagFromName(header.find<Attr::Compression>("none"));
203
if(!TIFFIsCODECConfigured(compression))
205
Aqsis::log() << warning << "No TIFF codec found for compression scheme \""
206
<< header.find<Attr::Compression>("none") << "\"\n";
209
setTiffTagValue<uint16>(TIFFTAG_COMPRESSION, compression);
211
if(compression == COMPRESSION_LZW || compression == COMPRESSION_DEFLATE)
213
// Add a compression predictor if possible; this drastically increases
214
// the compression ratios. Even though the online docs seem to suggest
215
// that predictors are independent of the compression codec, this is
216
// not the case for libtiff, which appears to give errors if predictors
217
// used with anything other than the lzw or deflate codecs.
219
// (the innards of libtiff suggest that TIFFPredictorInit() is only
220
// called by certian codecs)
222
// \todo Test whether PREDICTOR_FLOATINGPOINT is actually beneficial.
223
// (Some places on the web suggest not.)
224
if(header.channelList().sharedChannelType() == Channel_Float32)
225
setTiffTagValue<uint16>(TIFFTAG_PREDICTOR, PREDICTOR_FLOATINGPOINT);
227
setTiffTagValue<uint16>(TIFFTAG_PREDICTOR, PREDICTOR_HORIZONTAL);
229
if(compression == COMPRESSION_JPEG)
231
// Set the jpeg compression quality level if necessary.
232
setTiffTagValue<int>(TIFFTAG_JPEGQUALITY,
233
header.find<Attr::CompressionQuality>(85));
237
void CqTiffDirHandle::writeChannelAttrs(const CqTexFileHeader& header)
239
const CqChannelList& channelList = header.channelList();
240
EqChannelType channelType = channelList.sharedChannelType();
241
// Assume that the channel type is uniform across the various channels.
242
assert(channelType != Channel_TypeUnknown && channelType != Channel_Float16);
243
TqInt numChannels = channelList.numChannels();
244
assert(numChannels > 0);
246
setTiffTagValue<uint16>(TIFFTAG_SAMPLESPERPIXEL, numChannels);
247
setTiffTagValue<uint16>(TIFFTAG_BITSPERSAMPLE, 8*bytesPerPixel(channelType));
248
// It's hard to know which algorithm for deciding the photometric type is
249
// the best here. Perhaps it would be better to simply depend on the
250
// number of channels, since TIFF doesn't have a standard facility to store
252
if( (channelList.hasIntensityChannel() || numChannels <= 2)
253
&& !channelList.hasRgbChannel() )
256
setTiffTagValue<uint16>(TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
259
// Set extra sample types
260
std::vector<uint16> extraSamples(numChannels - 1, EXTRASAMPLE_UNSPECIFIED);
261
if(channelList[1].name == "a")
262
extraSamples[0] = EXTRASAMPLE_ASSOCALPHA;
263
setTiffTagValue(TIFFTAG_EXTRASAMPLES, extraSamples);
265
// \todo PHOTOMETRIC_LOGL alternative for floats
269
// Assume a colour image by default (use PHOTOMETRIC_RGB)
270
setTiffTagValue<uint16>(TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
271
/// \todo PHOTOMETRIC_LOGLUV alternative for floats
274
std::vector<uint16> extraSamples(numChannels - 3, EXTRASAMPLE_UNSPECIFIED);
275
// Set type of extra samples.
276
if(channelList[3].name == "a")
277
extraSamples[0] = EXTRASAMPLE_ASSOCALPHA;
280
// Initial support for setting extra samples for three channel
281
// alpha... This isn't likely to be terribly robust...
282
if(channelList[0].name == "r" && channelList[3].name == "ra")
283
extraSamples[0] = EXTRASAMPLE_ASSOCALPHA;
284
if(channelList[1].name == "g" && channelList[4].name == "ga")
285
extraSamples[1] = EXTRASAMPLE_ASSOCALPHA;
286
if(channelList[2].name == "b" && channelList[5].name == "ba")
287
extraSamples[2] = EXTRASAMPLE_ASSOCALPHA;
289
setTiffTagValue(TIFFTAG_EXTRASAMPLES, extraSamples);
292
/// \todo: deal with TIFFTAG_SGILOGDATAFMT
293
uint16 sampleFormat = 0;
296
case Channel_Float32:
297
sampleFormat = SAMPLEFORMAT_IEEEFP;
299
case Channel_Signed32:
300
case Channel_Signed16:
301
case Channel_Signed8:
302
sampleFormat = SAMPLEFORMAT_INT;
304
case Channel_Unsigned32:
305
case Channel_Unsigned16:
306
case Channel_Unsigned8:
307
sampleFormat = SAMPLEFORMAT_UINT;
310
AQSIS_THROW_XQERROR(XqInternal, EqE_Limit,
311
"Cannot handle provided pixel sample format");
314
setTiffTagValue<uint16>(TIFFTAG_SAMPLEFORMAT, sampleFormat);
320
/** Convert a type held in the header to the appropriate type understood by
323
template<typename Tattr, typename Ttiff>
324
Ttiff attrTypeToTiff(const Tattr& attr)
328
// Specialize for std::string -> const char*
330
const char* attrTypeToTiff(const std::string& attr)
334
// Specialize for CqMatrix -> TqFloat*
336
const float* attrTypeToTiff(const CqMatrix& attr)
338
return attr.pElements();
340
// Specialize for EqTextureFormat -> const char*
342
const char* attrTypeToTiff(const EqTextureFormat& format)
344
return texFormatToString(format);
348
* Add an attribute with the given tag and name from the header to the given
349
* tiff directory. If the attribute isn't present in the header, silently do
352
template<typename Tattr, typename Ttiff>
353
void addAttributeToTiff(ttag_t tag,
354
const CqTexFileHeader& header, CqTiffDirHandle& dirHandle)
356
const typename Tattr::type* headerVal = header.findPtr<Tattr>();
361
dirHandle.setTiffTagValue<Ttiff>(tag,
362
attrTypeToTiff<typename Tattr::type,Ttiff>(*headerVal));
366
Aqsis::log() << e << "\n";
371
} // unnamed namespace
374
void CqTiffDirHandle::writeOptionalAttrs(const CqTexFileHeader& header)
376
// Add various descriptive strings to the header if they exist
377
addAttributeToTiff<Attr::Software,const char*>(TIFFTAG_SOFTWARE, header, *this);
378
addAttributeToTiff<Attr::HostName,const char*>(TIFFTAG_HOSTCOMPUTER, header, *this);
379
addAttributeToTiff<Attr::Description,const char*>(TIFFTAG_IMAGEDESCRIPTION, header, *this);
380
addAttributeToTiff<Attr::DateTime,const char*>(TIFFTAG_DATETIME, header, *this);
381
addAttributeToTiff<Attr::TextureFormat,const char*>(TIFFTAG_PIXAR_TEXTUREFORMAT, header, *this);
383
/// \todo Consider the need for TIFFTAG_SMINSAMPLEVALUE and TIFFTAG_SMAXSAMPLEVALUE
385
// Add some matrix attributes
386
addAttributeToTiff<Attr::WorldToScreenMatrix,const float*>(
387
TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN, header, *this);
388
addAttributeToTiff<Attr::WorldToCameraMatrix,const float*>(
389
TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA, header, *this);
391
// Add cotan of the field of view.
392
addAttributeToTiff<Attr::FieldOfViewCot,float>(
393
TIFFTAG_PIXAR_FOVCOT, header, *this);
395
// Set texture wrap mode string
396
const SqWrapModes* wrapModes = header.findPtr<Attr::WrapModes>();
399
std::ostringstream oss;
400
oss << wrapModes->sWrap << " " << wrapModes->tWrap;
401
setTiffTagValue<const char*>(TIFFTAG_PIXAR_WRAPMODES, oss.str().c_str());
404
// Set size of display window if present
405
const SqImageRegion* displayWindow = header.findPtr<Attr::DisplayWindow>();
408
setTiffTagValue<uint32>(TIFFTAG_PIXAR_IMAGEFULLWIDTH, displayWindow->width);
409
setTiffTagValue<uint32>(TIFFTAG_PIXAR_IMAGEFULLLENGTH, displayWindow->height);
410
setTiffTagValue<float>(TIFFTAG_XPOSITION, displayWindow->topLeftX);
411
setTiffTagValue<float>(TIFFTAG_YPOSITION, displayWindow->topLeftY);
415
void CqTiffDirHandle::fillHeaderRequiredAttrs(CqTexFileHeader& header) const
417
// Fill header with general metadata which won't affect the details of the
418
// pixel memory layout.
419
header.setWidth(tiffTagValue<uint32>(TIFFTAG_IMAGEWIDTH));
420
header.setHeight(tiffTagValue<uint32>(TIFFTAG_IMAGELENGTH));
421
if(TIFFIsTiled(tiffPtr()))
423
header.set<Attr::TileInfo>( SqTileInfo(
424
tiffTagValue<uint32>(TIFFTAG_TILEWIDTH),
425
tiffTagValue<uint32>(TIFFTAG_TILELENGTH)) );
427
// Get the compression type.
428
header.set<Attr::Compression>(
429
tiffCompressionNameFromTag(tiffTagValue<uint16>(TIFFTAG_COMPRESSION)) );
430
// Compute pixel aspect ratio
433
if(TIFFGetField(tiffPtr(), TIFFTAG_XRESOLUTION, &xRes)
434
&& TIFFGetField(tiffPtr(), TIFFTAG_YRESOLUTION, &yRes))
436
// yRes/xRes should be the correct quantity corresponding to the
437
// pixelAspectRatio used in OpenEXR.
438
header.set<Attr::PixelAspectRatio>(yRes/xRes);
442
header.set<Attr::PixelAspectRatio>(1.0f);
449
template<typename Tattr, typename Ttiff>
450
typename Tattr::type attrTypeFromTiff(const Ttiff& tiffAttr)
452
return typename Tattr::type(tiffAttr);
454
// specialize for const char* -> EqTextureFormat
456
EqTextureFormat attrTypeFromTiff<Attr::TextureFormat, const char*>(
457
const char* const& texFormatStr)
459
return texFormatFromString(texFormatStr);
462
/// Extract an attribute from dirHandle and add it to header, if present.
463
template<typename Tattr, typename Ttiff>
464
void addAttributeToHeader(ttag_t tag, CqTexFileHeader& header,
465
const CqTiffDirHandle& dirHandle)
468
if(TIFFGetField(dirHandle.tiffPtr(), tag, &temp))
469
header.set<Tattr>(attrTypeFromTiff<Tattr, Ttiff>(temp));
472
/// Add texture wrap modes to the header if they can be found in the TIFF.
473
void addWrapModesToHeader(CqTexFileHeader& header, const CqTiffDirHandle& dirHandle)
475
char* wrapModesStr = 0;
476
if(TIFFGetField(dirHandle.tiffPtr(), TIFFTAG_PIXAR_WRAPMODES, &wrapModesStr))
478
std::istringstream iss(wrapModesStr);
480
iss >> modes.sWrap >> modes.tWrap;
481
header.set<Attr::WrapModes>(modes);
485
} // unnamed namespace
488
void CqTiffDirHandle::fillHeaderOptionalAttrs(CqTexFileHeader& header) const
490
// Add various descriptive strings to the header if they exist
491
addAttributeToHeader<Attr::Software,char*>(TIFFTAG_SOFTWARE, header, *this);
492
addAttributeToHeader<Attr::HostName,char*>(TIFFTAG_HOSTCOMPUTER, header, *this);
493
addAttributeToHeader<Attr::Description,char*>(TIFFTAG_IMAGEDESCRIPTION, header, *this);
494
addAttributeToHeader<Attr::DateTime,char*>(TIFFTAG_DATETIME, header, *this);
495
addAttributeToHeader<Attr::TextureFormat,const char*>(TIFFTAG_PIXAR_TEXTUREFORMAT, header, *this);
497
// Add texturemap-specific stuff to the header if it exists.
498
addWrapModesToHeader(header, *this);
500
// Add some matrix attributes
501
addAttributeToHeader<Attr::WorldToScreenMatrix,float*>(
502
TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN, header, *this);
503
addAttributeToHeader<Attr::WorldToCameraMatrix,float*>(
504
TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA, header, *this);
505
// Add cotan of the field of view.
506
addAttributeToHeader<Attr::FieldOfViewCot,float>(
507
TIFFTAG_PIXAR_FOVCOT, header, *this);
509
// Retrieve tags relevant to the display window
510
// The origin of the image is apparently given in resolution units, but
511
// here we want to interpret it as the number of pixels from the top left
512
// of the image, hence the lfloor.
513
uint32 fullWidth = header.width();
514
uint32 fullHeight = header.height();
517
if( TIFFGetField(tiffPtr(), TIFFTAG_PIXAR_IMAGEFULLWIDTH, &fullWidth)
518
| TIFFGetField(tiffPtr(), TIFFTAG_PIXAR_IMAGEFULLLENGTH, &fullHeight)
519
| TIFFGetField(tiffPtr(), TIFFTAG_XPOSITION, &xPos)
520
| TIFFGetField(tiffPtr(), TIFFTAG_YPOSITION, &yPos)
521
// bitwise OR used since we don't want shortcut evaluation
524
header.set<Attr::DisplayWindow>( SqImageRegion(fullWidth, fullHeight,
525
lfloor(xPos), lfloor(yPos)) );
529
void CqTiffDirHandle::fillHeaderPixelLayout(CqTexFileHeader& header) const
531
header.set<Attr::TiffUseGenericRGBA>(false);
532
// Deal with fields which determine the pixel layout.
535
// Deduce image channel information.
536
guessChannels(header.channelList());
537
// Check that channels are interlaced, otherwise we'll be confused.
538
TqInt planarConfig = tiffTagValue<uint16>(TIFFTAG_PLANARCONFIG,
539
PLANARCONFIG_CONTIG);
540
if(planarConfig != PLANARCONFIG_CONTIG)
541
AQSIS_THROW_XQERROR(XqUnknownTiffFormat, EqE_BadFile,
542
"non-interlaced channels detected");
543
// Check that the origin is at the topleft of the image.
544
TqInt orientation = tiffTagValue<uint16>(TIFFTAG_ORIENTATION,
545
ORIENTATION_TOPLEFT);
546
if(orientation != ORIENTATION_TOPLEFT)
548
Aqsis::log() << warning
549
<< "TIFF orientation for file \"" << m_fileHandle->fileName()
550
<< "\" is not top-left. This may result in unexpected results\n";
551
/// \todo Decide whether to use generic TIFF loading facilities for this.
552
//AQSIS_THROW_XQERROR(XqUnknownTiffFormat, EqE_Limit,
553
// "orientation isn't top-left");
556
catch(XqUnknownTiffFormat& e)
558
// The format is something strange that we don't know how to handle
559
// directly... Use the generic RGBA handling built into libtiff...
560
Aqsis::log() << warning
561
<< "Cannot handle desired tiff format efficiently: \"" << e.what() << "\".\n"
562
"Switching to generic RGBA handling - this may result in some loss of precision\n";
564
if(!TIFFRGBAImageOK(tiffPtr(), errBuf))
566
AQSIS_THROW_XQERROR(XqBadTexture, EqE_BadFile,
567
"Cannot use generic RGBA tiff interface for file \"" << m_fileHandle->fileName()
568
<< "\". " << "Libtiff says: " << errBuf);
570
EqChannelType chanType = Channel_Unsigned8;
571
CqChannelList& channelList = header.channelList();
573
channelList.addChannel( SqChannelInfo("r", chanType) );
574
channelList.addChannel( SqChannelInfo("g", chanType) );
575
channelList.addChannel( SqChannelInfo("b", chanType) );
576
channelList.addChannel( SqChannelInfo("a", chanType) );
577
header.set<Attr::TiffUseGenericRGBA>(true);
581
EqChannelType CqTiffDirHandle::guessChannelType() const
583
TqInt bitsPerSample = tiffTagValue<uint16>(TIFFTAG_BITSPERSAMPLE);
584
TqInt sampleFormat = tiffTagValue<uint16>(TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
585
switch(bitsPerSample)
590
case SAMPLEFORMAT_IEEEFP:
591
return Channel_Float32;
592
case SAMPLEFORMAT_INT:
593
return Channel_Signed32;
594
case SAMPLEFORMAT_UINT:
595
return Channel_Unsigned32;
597
Aqsis::log() << warning
598
<< "Unknown tiff format for 32 bits per sample: "
599
"TIFFTAG_SAMPLEFORMAT = " << sampleFormat
600
<< ". Assuming unsigned int.\n";
601
return Channel_Unsigned32;
607
case SAMPLEFORMAT_INT:
608
return Channel_Signed16;
609
case SAMPLEFORMAT_UINT:
610
return Channel_Unsigned16;
612
Aqsis::log() << warning
613
<< "Unknown tiff format for 16 bits per sample: "
614
"TIFFTAG_SAMPLEFORMAT = " << sampleFormat
615
<< ". Assuming unsigned int.\n";
616
return Channel_Unsigned16;
622
case SAMPLEFORMAT_INT:
623
return Channel_Signed8;
624
case SAMPLEFORMAT_UINT:
625
return Channel_Unsigned8;
627
Aqsis::log() << warning
628
<< "Unknown tiff format for 8 bits per sample: "
629
"TIFFTAG_SAMPLEFORMAT = " << sampleFormat
630
<< ". Assuming unsigned int.\n";
631
return Channel_Unsigned8;
635
// We give up and use the generic 8bpp tiff loading.
636
return Channel_TypeUnknown;
640
void CqTiffDirHandle::guessChannels(CqChannelList& channelList) const
643
EqChannelType chanType = guessChannelType();
644
if(chanType == Channel_TypeUnknown)
645
AQSIS_THROW_XQERROR(XqUnknownTiffFormat, EqE_Limit,
646
"Cannot determine channel type");
649
// Determine the channel type held in the tiff
650
switch(tiffTagValue<uint16>(TIFFTAG_PHOTOMETRIC))
652
case PHOTOMETRIC_MINISBLACK:
654
TqInt samplesPerPixel = tiffTagValue<uint16>(TIFFTAG_SAMPLESPERPIXEL, 1);
655
// We have an intensity (y) channel only.
656
channelList.addChannel(SqChannelInfo("y", chanType));
657
if(samplesPerPixel == 2)
659
// For two channels, assume the second is alpha
660
channelList.addChannel(SqChannelInfo("a", chanType));
664
// Otherwise we're a bit confused; just add the
665
// additional channels as unnamed.
666
channelList.addUnnamedChannels(chanType, samplesPerPixel-1);
670
case PHOTOMETRIC_RGB:
672
TqInt samplesPerPixel = tiffTagValue<uint16>(TIFFTAG_SAMPLESPERPIXEL);
673
if(samplesPerPixel < 3)
674
channelList.addUnnamedChannels(chanType, samplesPerPixel);
678
channelList.addChannel(SqChannelInfo("r", chanType));
679
channelList.addChannel(SqChannelInfo("g", chanType));
680
channelList.addChannel(SqChannelInfo("b", chanType));
681
/// \todo Investigate what to do about TIFFTAG_EXTRASAMPLES
682
if(samplesPerPixel == 4)
685
channelList.addChannel(SqChannelInfo("a", chanType));
687
else if(samplesPerPixel == 6)
689
// add RGB alpha channels
690
channelList.addChannel(SqChannelInfo("ra", chanType));
691
channelList.addChannel(SqChannelInfo("ga", chanType));
692
channelList.addChannel(SqChannelInfo("ba", chanType));
696
// Or not sure what to do here... add some unnamed
698
channelList.addUnnamedChannels(chanType, samplesPerPixel-3);
703
/// \todo Should also handle the following?
704
//case PHOTOMETRIC_LOGL:
705
//case PHOTOMETRIC_LOGLUV:
707
AQSIS_THROW_XQERROR(XqUnknownTiffFormat, EqE_Limit,
708
"Unknown photometric type");
714
//------------------------------------------------------------------------------
716
//------------------------------------------------------------------------------
720
void safeTiffClose(TIFF* tif)
726
} // unnamed namespace
728
CqTiffFileHandle::CqTiffFileHandle(const boostfs::path& fileName, const char* openMode)
729
: m_fileName(fileName),
730
m_tiffPtr(TIFFOpen(fileName.file_string().c_str(), openMode), safeTiffClose),
731
m_isInputFile(openMode[0] == 'r'),
735
AQSIS_THROW_XQERROR(XqInvalidFile, EqE_NoFile,
736
"Could not open tiff file \"" << fileName << "\"");
739
CqTiffFileHandle::CqTiffFileHandle(std::istream& inputStream)
740
: m_tiffPtr(TIFFStreamOpen("stream", &inputStream), safeTiffClose),
746
AQSIS_THROW_XQERROR(XqInternal, EqE_NoFile,
747
"Could not use input stream for tiff");
751
CqTiffFileHandle::CqTiffFileHandle(std::ostream& outputStream)
752
: m_tiffPtr(TIFFStreamOpen("stream", &outputStream), safeTiffClose),
753
m_isInputFile(false),
758
AQSIS_THROW_XQERROR(XqInternal, EqE_NoFile,
759
"Could not use output stream for tiff");
764
void CqTiffFileHandle::writeDirectory()
766
assert(!m_isInputFile);
767
if(!TIFFWriteDirectory(m_tiffPtr.get()))
768
AQSIS_THROW_XQERROR(XqInternal, EqE_BadFile,
769
"Could not write tiff subimage to file");
774
tdir_t CqTiffFileHandle::numDirectories()
776
return TIFFNumberOfDirectories(m_tiffPtr.get());
780
void CqTiffFileHandle::setDirectory(tdir_t dirIdx)
782
if(m_isInputFile && dirIdx != m_currDir)
784
if(!TIFFSetDirectory(m_tiffPtr.get(), dirIdx))
786
AQSIS_THROW_XQERROR(XqInternal, EqE_Bug,
787
"Requested tiff directory " << dirIdx << " out of range for file \""
788
<< m_fileName << "\"");
794
//------------------------------------------------------------------------------
797
void stridedCopy(TqUint8* dest, TqInt destStride, const TqUint8* src, TqInt srcStride,
798
TqInt numElems, TqInt elemSize)
800
for(TqInt i = 0; i < numElems; ++i)
802
std::memcpy(dest, src, elemSize);
808
//------------------------------------------------------------------------------