~ubuntu-branches/ubuntu/quantal/aqsis/quantal

« back to all changes in this revision

Viewing changes to libs/tex/io/tiffdirhandle.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Fabrice Coutadeur
  • Date: 2009-08-06 04:53:26 UTC
  • mfrom: (1.2.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20090806045326-z6xeaaao62idxcc6
Tags: 1.6.0-0ubuntu1
* New upstream release
* debian/control:
  - changed name of lib package to libaqsis1 instead of aqsis-libsc2a
  - changed name of dev package to libaqsis-dev instead of aqsis-libs-dev
  - Added aqsis-data package
  - Revised summary text according to that specified by the RISpec (Pixar)
* Moved examples installation from aqsis.install to aqsis-data.install
* debian/rules: 
  - added content to binary-indep target
* debian/rules: added explicit name of mime file to force dh_installmime
  to generate postinst and prerm scripts

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Aqsis
 
2
// Copyright (C) 1997 - 2007, Paul C. Gregory
 
3
//
 
4
// Contact: pgregory@aqsis.org
 
5
//
 
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.
 
10
//
 
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.
 
15
//
 
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
 
19
 
 
20
/** \file
 
21
 *
 
22
 * \brief A C++ wrapper around tiff files for the functions of interest in aqsis.
 
23
 *
 
24
 * \author Chris Foster
 
25
 */
 
26
 
 
27
#include "tiffdirhandle.h"
 
28
 
 
29
#include <sstream>
 
30
#include <cstring>  // for memcpy()
 
31
 
 
32
#include <tiffio.hxx>
 
33
 
 
34
#include <aqsis/math/math.h>
 
35
#include <aqsis/math/matrix.h>
 
36
#include <aqsis/util/logging.h>
 
37
#include <aqsis/tex/texexception.h>
 
38
 
 
39
namespace Aqsis
 
40
{
 
41
 
 
42
/// An exception for internal usage by the tiff handling code.
 
43
AQSIS_DECLARE_XQEXCEPTION(XqUnknownTiffFormat, XqInternal);
 
44
 
 
45
//------------------------------------------------------------------------------
 
46
// Helper functions and data for dealing with tiff <--> header conversions.
 
47
//------------------------------------------------------------------------------
 
48
namespace {
 
49
 
 
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";
 
56
 
 
57
/// Convert from a string to an EqTextureFormat
 
58
EqTextureFormat texFormatFromString(const std::string& str)
 
59
{
 
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;
 
71
}
 
72
 
 
73
/// Convert from an EqTextureFormat to a string.
 
74
const char* texFormatToString(EqTextureFormat format)
 
75
{
 
76
        switch(format)
 
77
        {
 
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:
 
89
                        return "unknown";
 
90
        }
 
91
        assert("unhandled format type" && 0);
 
92
        return "unknown"; // shut up compiler warning.
 
93
}
 
94
 
 
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"),
 
103
};
 
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));
 
107
 
 
108
/// Get the tiff compression type from a string description.
 
109
uint16 tiffCompressionTagFromName(const std::string& compressionName)
 
110
{
 
111
        for(std::vector<TqComprPair>::const_iterator i = compressionTypes.begin();
 
112
                        i != compressionTypes.end(); ++i)
 
113
        {
 
114
                if(i->second == compressionName)
 
115
                        return i->first;
 
116
        }
 
117
        return COMPRESSION_NONE;
 
118
}
 
119
 
 
120
/// Get the tiff compression type from a string description.
 
121
const char* tiffCompressionNameFromTag(uint16 compressionType)
 
122
{
 
123
        for(std::vector<TqComprPair>::const_iterator i = compressionTypes.begin();
 
124
                        i != compressionTypes.end(); ++i)
 
125
        {
 
126
                if(i->first == compressionType)
 
127
                        return i->second;
 
128
        }
 
129
        return "unknown";
 
130
}
 
131
 
 
132
} // unnamed namespace
 
133
 
 
134
//------------------------------------------------------------------------------
 
135
// CqTiffDirHandle
 
136
//------------------------------------------------------------------------------
 
137
 
 
138
CqTiffDirHandle::CqTiffDirHandle(const boost::shared_ptr<CqTiffFileHandle>& fileHandle, const tdir_t dirIdx)
 
139
        : m_fileHandle(fileHandle)
 
140
{
 
141
        fileHandle->setDirectory(dirIdx);
 
142
}
 
143
 
 
144
tdir_t CqTiffDirHandle::dirIndex() const
 
145
{
 
146
        return m_fileHandle->m_currDir;
 
147
}
 
148
 
 
149
void CqTiffDirHandle::fillHeader(CqTexFileHeader& header) const
 
150
{
 
151
        fillHeaderRequiredAttrs(header);
 
152
        fillHeaderOptionalAttrs(header);
 
153
        fillHeaderPixelLayout(header);
 
154
}
 
155
 
 
156
void CqTiffDirHandle::writeHeader(const CqTexFileHeader& header)
 
157
{
 
158
        writeRequiredAttrs(header);
 
159
        writeOptionalAttrs(header);
 
160
}
 
161
 
 
162
void CqTiffDirHandle::writeRequiredAttrs(const CqTexFileHeader& header)
 
163
{
 
164
        // Width, height...
 
165
        setTiffTagValue<uint32>(TIFFTAG_IMAGEWIDTH, header.width());
 
166
        setTiffTagValue<uint32>(TIFFTAG_IMAGELENGTH, header.height());
 
167
 
 
168
        // Orientation & planar config should always be fixed.
 
169
        setTiffTagValue<uint16>(TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
 
170
        setTiffTagValue<uint16>(TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
 
171
 
 
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));
 
178
 
 
179
        // Compression-related stuff
 
180
        writeCompressionAttrs(header);
 
181
        // Channel-related stuff
 
182
        writeChannelAttrs(header);
 
183
 
 
184
        const SqTileInfo* tileInfo = header.findPtr<Attr::TileInfo>();
 
185
        if(tileInfo)
 
186
        {
 
187
                // Set tile dimensions if present.
 
188
                setTiffTagValue<uint32>(TIFFTAG_TILEWIDTH, tileInfo->width);
 
189
                setTiffTagValue<uint32>(TIFFTAG_TILELENGTH, tileInfo->height);
 
190
        }
 
191
        else
 
192
        {
 
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));
 
196
        }
 
197
}
 
198
 
 
199
void CqTiffDirHandle::writeCompressionAttrs(const CqTexFileHeader& header)
 
200
{
 
201
        // Set the compression type.
 
202
        uint16 compression = tiffCompressionTagFromName(header.find<Attr::Compression>("none"));
 
203
        if(!TIFFIsCODECConfigured(compression))
 
204
        {
 
205
                Aqsis::log() << warning << "No TIFF codec found for compression scheme \""
 
206
                        << header.find<Attr::Compression>("none") << "\"\n";
 
207
                return;
 
208
        }
 
209
        setTiffTagValue<uint16>(TIFFTAG_COMPRESSION, compression);
 
210
 
 
211
        if(compression == COMPRESSION_LZW || compression == COMPRESSION_DEFLATE)
 
212
        {
 
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.
 
218
                //
 
219
                // (the innards of libtiff suggest that TIFFPredictorInit() is only
 
220
                // called by certian codecs)
 
221
                //
 
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);
 
226
                else
 
227
                        setTiffTagValue<uint16>(TIFFTAG_PREDICTOR, PREDICTOR_HORIZONTAL);
 
228
        }
 
229
        if(compression == COMPRESSION_JPEG)
 
230
        {
 
231
                // Set the jpeg compression quality level if necessary.
 
232
                setTiffTagValue<int>(TIFFTAG_JPEGQUALITY,
 
233
                                header.find<Attr::CompressionQuality>(85));
 
234
        }
 
235
}
 
236
 
 
237
void CqTiffDirHandle::writeChannelAttrs(const CqTexFileHeader& header)
 
238
{
 
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);
 
245
 
 
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
 
251
        // channel names...
 
252
        if( (channelList.hasIntensityChannel() || numChannels <= 2)
 
253
                        && !channelList.hasRgbChannel() )
 
254
        {
 
255
                // greyscale image
 
256
                setTiffTagValue<uint16>(TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
 
257
                if(numChannels == 2)
 
258
                {
 
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);
 
264
                }
 
265
                // \todo PHOTOMETRIC_LOGL alternative for floats
 
266
        }
 
267
        else
 
268
        {
 
269
                // Assume a colour image by default (use PHOTOMETRIC_RGB)
 
270
                setTiffTagValue<uint16>(TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
 
271
                /// \todo PHOTOMETRIC_LOGLUV alternative for floats
 
272
                if(numChannels > 3)
 
273
                {
 
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;
 
278
                        if(numChannels >= 6)
 
279
                        {
 
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;
 
288
                        }
 
289
                        setTiffTagValue(TIFFTAG_EXTRASAMPLES, extraSamples);
 
290
                }
 
291
        }
 
292
        /// \todo: deal with TIFFTAG_SGILOGDATAFMT
 
293
        uint16 sampleFormat = 0;
 
294
        switch(channelType)
 
295
        {
 
296
        case Channel_Float32:
 
297
                        sampleFormat = SAMPLEFORMAT_IEEEFP;
 
298
                        break;
 
299
        case Channel_Signed32:
 
300
        case Channel_Signed16:
 
301
        case Channel_Signed8:
 
302
                        sampleFormat = SAMPLEFORMAT_INT;
 
303
                        break;
 
304
        case Channel_Unsigned32:
 
305
        case Channel_Unsigned16:
 
306
        case Channel_Unsigned8:
 
307
                        sampleFormat = SAMPLEFORMAT_UINT;
 
308
                        break;
 
309
                default:
 
310
                        AQSIS_THROW_XQERROR(XqInternal, EqE_Limit,
 
311
                                "Cannot handle provided pixel sample format");
 
312
                        break;
 
313
    }
 
314
        setTiffTagValue<uint16>(TIFFTAG_SAMPLEFORMAT, sampleFormat);
 
315
}
 
316
 
 
317
 
 
318
namespace {
 
319
 
 
320
/** Convert a type held in the header to the appropriate type understood by
 
321
 * libtiff.
 
322
 */
 
323
template<typename Tattr, typename Ttiff>
 
324
Ttiff attrTypeToTiff(const Tattr& attr)
 
325
{
 
326
        return Ttiff(attr);
 
327
}
 
328
// Specialize for std::string -> const char*
 
329
template<>
 
330
const char* attrTypeToTiff(const std::string& attr)
 
331
{
 
332
        return attr.c_str();
 
333
}
 
334
// Specialize for CqMatrix -> TqFloat*
 
335
template<>
 
336
const float* attrTypeToTiff(const CqMatrix& attr)
 
337
{
 
338
        return attr.pElements();
 
339
}
 
340
// Specialize for EqTextureFormat -> const char*
 
341
template<>
 
342
const char* attrTypeToTiff(const EqTextureFormat& format)
 
343
{
 
344
        return texFormatToString(format);
 
345
}
 
346
 
 
347
/**
 
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
 
350
 * nothing.
 
351
 */
 
352
template<typename Tattr, typename Ttiff>
 
353
void addAttributeToTiff(ttag_t tag,
 
354
                const CqTexFileHeader& header, CqTiffDirHandle& dirHandle)
 
355
{
 
356
        const typename Tattr::type* headerVal = header.findPtr<Tattr>();
 
357
        if(headerVal)
 
358
        {
 
359
                try
 
360
                {
 
361
                        dirHandle.setTiffTagValue<Ttiff>(tag, 
 
362
                                        attrTypeToTiff<typename Tattr::type,Ttiff>(*headerVal));
 
363
                }
 
364
                catch(XqInternal& e)
 
365
                {
 
366
                        Aqsis::log() << e << "\n";
 
367
                }
 
368
        }
 
369
}
 
370
 
 
371
} // unnamed namespace
 
372
 
 
373
 
 
374
void CqTiffDirHandle::writeOptionalAttrs(const CqTexFileHeader& header)
 
375
{
 
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);
 
382
 
 
383
        /// \todo Consider the need for TIFFTAG_SMINSAMPLEVALUE and TIFFTAG_SMAXSAMPLEVALUE
 
384
 
 
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);
 
390
 
 
391
        // Add cotan of the field of view.
 
392
        addAttributeToTiff<Attr::FieldOfViewCot,float>(
 
393
                        TIFFTAG_PIXAR_FOVCOT, header, *this);
 
394
 
 
395
        // Set texture wrap mode string
 
396
        const SqWrapModes* wrapModes = header.findPtr<Attr::WrapModes>();
 
397
        if(wrapModes)
 
398
        {
 
399
                std::ostringstream oss;
 
400
                oss << wrapModes->sWrap << " " << wrapModes->tWrap;
 
401
                setTiffTagValue<const char*>(TIFFTAG_PIXAR_WRAPMODES, oss.str().c_str());
 
402
        }
 
403
 
 
404
        // Set size of display window if present
 
405
        const SqImageRegion* displayWindow = header.findPtr<Attr::DisplayWindow>();
 
406
        if(displayWindow)
 
407
        {
 
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);
 
412
        }
 
413
}
 
414
 
 
415
void CqTiffDirHandle::fillHeaderRequiredAttrs(CqTexFileHeader& header) const
 
416
{
 
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()))
 
422
        {
 
423
                header.set<Attr::TileInfo>( SqTileInfo(
 
424
                                        tiffTagValue<uint32>(TIFFTAG_TILEWIDTH),
 
425
                                        tiffTagValue<uint32>(TIFFTAG_TILELENGTH)) );
 
426
        }
 
427
        // Get the compression type.
 
428
        header.set<Attr::Compression>(
 
429
                        tiffCompressionNameFromTag(tiffTagValue<uint16>(TIFFTAG_COMPRESSION)) );
 
430
        // Compute pixel aspect ratio
 
431
        TqFloat xRes = 0;
 
432
        TqFloat yRes = 0;
 
433
        if(TIFFGetField(tiffPtr(), TIFFTAG_XRESOLUTION, &xRes)
 
434
                        && TIFFGetField(tiffPtr(), TIFFTAG_YRESOLUTION, &yRes))
 
435
        {
 
436
                // yRes/xRes should be the correct quantity corresponding to the
 
437
                // pixelAspectRatio used in OpenEXR.
 
438
                header.set<Attr::PixelAspectRatio>(yRes/xRes);
 
439
        }
 
440
        else
 
441
        {
 
442
                header.set<Attr::PixelAspectRatio>(1.0f);
 
443
        }
 
444
}
 
445
 
 
446
 
 
447
namespace {
 
448
 
 
449
template<typename Tattr, typename Ttiff>
 
450
typename Tattr::type attrTypeFromTiff(const Ttiff& tiffAttr)
 
451
{
 
452
        return typename Tattr::type(tiffAttr);
 
453
}
 
454
// specialize for const char* -> EqTextureFormat
 
455
template<>
 
456
EqTextureFormat attrTypeFromTiff<Attr::TextureFormat, const char*>(
 
457
                const char* const& texFormatStr)
 
458
{
 
459
        return texFormatFromString(texFormatStr);
 
460
}
 
461
 
 
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)
 
466
{
 
467
        Ttiff temp;
 
468
        if(TIFFGetField(dirHandle.tiffPtr(), tag, &temp))
 
469
                header.set<Tattr>(attrTypeFromTiff<Tattr, Ttiff>(temp));
 
470
}
 
471
 
 
472
/// Add texture wrap modes to the header if they can be found in the TIFF.
 
473
void addWrapModesToHeader(CqTexFileHeader& header, const CqTiffDirHandle& dirHandle)
 
474
{
 
475
        char* wrapModesStr = 0;
 
476
        if(TIFFGetField(dirHandle.tiffPtr(), TIFFTAG_PIXAR_WRAPMODES, &wrapModesStr))
 
477
        {
 
478
                std::istringstream iss(wrapModesStr);
 
479
                SqWrapModes modes;
 
480
                iss >> modes.sWrap >> modes.tWrap;
 
481
                header.set<Attr::WrapModes>(modes);
 
482
        }
 
483
}
 
484
 
 
485
} // unnamed namespace
 
486
 
 
487
 
 
488
void CqTiffDirHandle::fillHeaderOptionalAttrs(CqTexFileHeader& header) const
 
489
{
 
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);
 
496
 
 
497
        // Add texturemap-specific stuff to the header if it exists.
 
498
        addWrapModesToHeader(header, *this);
 
499
 
 
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);
 
508
 
 
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();
 
515
        float xPos = 0;
 
516
        float yPos = 0;
 
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
 
522
                )
 
523
        {
 
524
                header.set<Attr::DisplayWindow>( SqImageRegion(fullWidth, fullHeight,
 
525
                                        lfloor(xPos), lfloor(yPos)) );
 
526
        }
 
527
}
 
528
 
 
529
void CqTiffDirHandle::fillHeaderPixelLayout(CqTexFileHeader& header) const
 
530
{
 
531
        header.set<Attr::TiffUseGenericRGBA>(false);
 
532
        // Deal with fields which determine the pixel layout.
 
533
        try
 
534
        {
 
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)
 
547
                {
 
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");
 
554
                }
 
555
        }
 
556
        catch(XqUnknownTiffFormat& e)
 
557
        {
 
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";
 
563
                char errBuf[1024];
 
564
                if(!TIFFRGBAImageOK(tiffPtr(), errBuf))
 
565
                {
 
566
                        AQSIS_THROW_XQERROR(XqBadTexture, EqE_BadFile,
 
567
                                "Cannot use generic RGBA tiff interface for file \"" << m_fileHandle->fileName()
 
568
                                << "\".  " << "Libtiff says: " << errBuf);
 
569
                }
 
570
                EqChannelType chanType = Channel_Unsigned8;
 
571
                CqChannelList& channelList = header.channelList();
 
572
                channelList.clear();
 
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);
 
578
        }
 
579
}
 
580
 
 
581
EqChannelType CqTiffDirHandle::guessChannelType() const
 
582
{
 
583
        TqInt bitsPerSample = tiffTagValue<uint16>(TIFFTAG_BITSPERSAMPLE);
 
584
        TqInt sampleFormat = tiffTagValue<uint16>(TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
 
585
        switch(bitsPerSample)
 
586
        {
 
587
                case 32:
 
588
                        switch(sampleFormat)
 
589
                        {
 
590
                                case SAMPLEFORMAT_IEEEFP:
 
591
                                        return Channel_Float32;
 
592
                                case SAMPLEFORMAT_INT:
 
593
                                        return Channel_Signed32;
 
594
                                case SAMPLEFORMAT_UINT:
 
595
                                        return Channel_Unsigned32;
 
596
                                default:
 
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;
 
602
                        }
 
603
                        break;
 
604
                case 16:
 
605
                        switch(sampleFormat)
 
606
                        {
 
607
                                case SAMPLEFORMAT_INT:
 
608
                                        return Channel_Signed16;
 
609
                                case SAMPLEFORMAT_UINT:
 
610
                                        return Channel_Unsigned16;
 
611
                                default:
 
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;
 
617
                        }
 
618
                        break;
 
619
                case 8:
 
620
                        switch(sampleFormat)
 
621
                        {
 
622
                                case SAMPLEFORMAT_INT:
 
623
                                        return Channel_Signed8;
 
624
                                case SAMPLEFORMAT_UINT:
 
625
                                        return Channel_Unsigned8;
 
626
                                default:
 
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;
 
632
                        }
 
633
                        break;
 
634
                default:
 
635
                        // We give up and use the generic 8bpp tiff loading.
 
636
                        return Channel_TypeUnknown;
 
637
        }
 
638
}
 
639
 
 
640
void CqTiffDirHandle::guessChannels(CqChannelList& channelList) const
 
641
{
 
642
        channelList.clear();
 
643
        EqChannelType chanType = guessChannelType();
 
644
        if(chanType == Channel_TypeUnknown)
 
645
                AQSIS_THROW_XQERROR(XqUnknownTiffFormat, EqE_Limit,
 
646
                        "Cannot determine channel type");
 
647
        else
 
648
        {
 
649
                // Determine the channel type held in the tiff
 
650
                switch(tiffTagValue<uint16>(TIFFTAG_PHOTOMETRIC))
 
651
                {
 
652
                        case PHOTOMETRIC_MINISBLACK:
 
653
                                {
 
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)
 
658
                                        {
 
659
                                                // For two channels, assume the second is alpha
 
660
                                                channelList.addChannel(SqChannelInfo("a", chanType));
 
661
                                        }
 
662
                                        else
 
663
                                        {
 
664
                                                // Otherwise we're a bit confused; just add the
 
665
                                                // additional channels as unnamed.
 
666
                                                channelList.addUnnamedChannels(chanType, samplesPerPixel-1);
 
667
                                        }
 
668
                                }
 
669
                                break;
 
670
                        case PHOTOMETRIC_RGB:
 
671
                                {
 
672
                                        TqInt samplesPerPixel = tiffTagValue<uint16>(TIFFTAG_SAMPLESPERPIXEL);
 
673
                                        if(samplesPerPixel < 3)
 
674
                                                channelList.addUnnamedChannels(chanType, samplesPerPixel);
 
675
                                        else
 
676
                                        {
 
677
                                                // add RGB channels
 
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)
 
683
                                                {
 
684
                                                        // add alpha channel
 
685
                                                        channelList.addChannel(SqChannelInfo("a", chanType));
 
686
                                                }
 
687
                                                else if(samplesPerPixel == 6)
 
688
                                                {
 
689
                                                        // add RGB alpha channels
 
690
                                                        channelList.addChannel(SqChannelInfo("ra", chanType));
 
691
                                                        channelList.addChannel(SqChannelInfo("ga", chanType));
 
692
                                                        channelList.addChannel(SqChannelInfo("ba", chanType));
 
693
                                                }
 
694
                                                else
 
695
                                                {
 
696
                                                        // Or not sure what to do here... add some unnamed
 
697
                                                        // channels?
 
698
                                                        channelList.addUnnamedChannels(chanType, samplesPerPixel-3);
 
699
                                                }
 
700
                                        }
 
701
                                }
 
702
                                break;
 
703
                        /// \todo Should also handle the following?
 
704
                        //case PHOTOMETRIC_LOGL:
 
705
                        //case PHOTOMETRIC_LOGLUV:
 
706
                        default:
 
707
                                AQSIS_THROW_XQERROR(XqUnknownTiffFormat, EqE_Limit,
 
708
                                        "Unknown photometric type");
 
709
                }
 
710
        }
 
711
}
 
712
 
 
713
 
 
714
//------------------------------------------------------------------------------
 
715
// CqTiffFileHandle
 
716
//------------------------------------------------------------------------------
 
717
 
 
718
namespace {
 
719
 
 
720
void safeTiffClose(TIFF* tif)
 
721
{
 
722
        if(tif)
 
723
                TIFFClose(tif);
 
724
}
 
725
 
 
726
} // unnamed namespace
 
727
 
 
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'),
 
732
        m_currDir(0)
 
733
{
 
734
        if(!m_tiffPtr)
 
735
                AQSIS_THROW_XQERROR(XqInvalidFile, EqE_NoFile,
 
736
                        "Could not open tiff file \"" << fileName << "\"");
 
737
}
 
738
 
 
739
CqTiffFileHandle::CqTiffFileHandle(std::istream& inputStream)
 
740
        : m_tiffPtr(TIFFStreamOpen("stream", &inputStream), safeTiffClose),
 
741
        m_isInputFile(true),
 
742
        m_currDir(0)
 
743
{
 
744
        if(!m_tiffPtr)
 
745
        {
 
746
                AQSIS_THROW_XQERROR(XqInternal, EqE_NoFile,
 
747
                        "Could not use input stream for tiff");
 
748
        }
 
749
}
 
750
 
 
751
CqTiffFileHandle::CqTiffFileHandle(std::ostream& outputStream)
 
752
        : m_tiffPtr(TIFFStreamOpen("stream", &outputStream), safeTiffClose),
 
753
        m_isInputFile(false),
 
754
        m_currDir(0)
 
755
{
 
756
        if(!m_tiffPtr)
 
757
        {
 
758
                AQSIS_THROW_XQERROR(XqInternal, EqE_NoFile,
 
759
                        "Could not use output stream for tiff");
 
760
        }
 
761
}
 
762
 
 
763
 
 
764
void CqTiffFileHandle::writeDirectory()
 
765
{
 
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");
 
770
        ++m_currDir;
 
771
}
 
772
 
 
773
 
 
774
tdir_t CqTiffFileHandle::numDirectories()
 
775
{
 
776
        return TIFFNumberOfDirectories(m_tiffPtr.get());
 
777
}
 
778
 
 
779
 
 
780
void CqTiffFileHandle::setDirectory(tdir_t dirIdx)
 
781
{
 
782
        if(m_isInputFile && dirIdx != m_currDir)
 
783
        {
 
784
                if(!TIFFSetDirectory(m_tiffPtr.get(), dirIdx))
 
785
                {
 
786
                        AQSIS_THROW_XQERROR(XqInternal, EqE_Bug,
 
787
                                        "Requested tiff directory " << dirIdx << " out of range for file \""
 
788
                                        << m_fileName << "\"");
 
789
                }
 
790
                m_currDir = dirIdx;
 
791
        }
 
792
}
 
793
 
 
794
//------------------------------------------------------------------------------
 
795
// Free functions
 
796
 
 
797
void stridedCopy(TqUint8* dest, TqInt destStride, const TqUint8* src, TqInt srcStride,
 
798
                TqInt numElems, TqInt elemSize)
 
799
{
 
800
        for(TqInt i = 0; i < numElems; ++i)
 
801
        {
 
802
                std::memcpy(dest, src, elemSize);
 
803
                dest += destStride;
 
804
                src += srcStride;
 
805
        }
 
806
}
 
807
 
 
808
//------------------------------------------------------------------------------
 
809
} // namespace Aqsis