Coverage Report

Created: 2023-12-08 06:53

/src/freeimage-svn/FreeImage/trunk/Source/OpenEXR/IlmImf/ImfMultiPartInputFile.cpp
Line
Count
Source (jump to first uncovered line)
1
///////////////////////////////////////////////////////////////////////////
2
//
3
// Copyright (c) 2011, Industrial Light & Magic, a division of Lucas
4
// Digital Ltd. LLC
5
//
6
// All rights reserved.
7
//
8
// Redistribution and use in source and binary forms, with or without
9
// modification, are permitted provided that the following conditions are
10
// met:
11
// *       Redistributions of source code must retain the above copyright
12
// notice, this list of conditions and the following disclaimer.
13
// *       Redistributions in binary form must reproduce the above
14
// copyright notice, this list of conditions and the following disclaimer
15
// in the documentation and/or other materials provided with the
16
// distribution.
17
// *       Neither the name of Industrial Light & Magic nor the names of
18
// its contributors may be used to endorse or promote products derived
19
// from this software without specific prior written permission.
20
//
21
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
//
33
///////////////////////////////////////////////////////////////////////////
34
35
#include "ImfMultiPartInputFile.h"
36
37
#include "ImfTimeCodeAttribute.h"
38
#include "ImfChromaticitiesAttribute.h"
39
#include "ImfBoxAttribute.h"
40
#include "ImfFloatAttribute.h"
41
#include "ImfStdIO.h"
42
#include "ImfTileOffsets.h"
43
#include "ImfMisc.h"
44
#include "ImfTiledMisc.h"
45
#include "ImfInputStreamMutex.h"
46
#include "ImfInputPartData.h"
47
#include "ImfPartType.h"
48
#include "ImfInputFile.h"
49
#include "ImfScanLineInputFile.h"
50
#include "ImfTiledInputFile.h"
51
#include "ImfDeepScanLineInputFile.h"
52
#include "ImfDeepTiledInputFile.h"
53
#include "ImfVersion.h"
54
55
#include <OpenEXRConfig.h>
56
#include <IlmThread.h>
57
#include <IlmThreadMutex.h>
58
59
#include <Iex.h>
60
#include <map>
61
#include <set>
62
63
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
64
65
using ILMTHREAD_NAMESPACE::Mutex;
66
using ILMTHREAD_NAMESPACE::Lock;
67
using IMATH_NAMESPACE::Box2i;
68
69
using std::vector;
70
using std::map;
71
using std::set;
72
using std::string;
73
74
namespace
75
{
76
    // Controls whether we error out in the event of shared attribute
77
    // inconsistency in the input file
78
    static const bool strictSharedAttribute = true;
79
}
80
81
struct MultiPartInputFile::Data: public InputStreamMutex
82
{
83
    int                         version;        // Version of this file.
84
    bool                        deleteStream;   // If we should delete the stream during destruction.
85
    vector<InputPartData*>      parts;          // Data to initialize Output files.
86
    int                         numThreads;     // Number of threads
87
    bool                        reconstructChunkOffsetTable;    // If we should reconstruct
88
                                                                // the offset table if it's broken.
89
    std::map<int,GenericInputFile*> _inputFiles;
90
    std::vector<Header>             _headers;
91
92
    
93
    void                    chunkOffsetReconstruction(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is, const std::vector<InputPartData*>& parts);
94
                                                      
95
    void                    readChunkOffsetTables(bool reconstructChunkOffsetTable);
96
                                                      
97
    bool                    checkSharedAttributesValues(const Header & src,
98
                                                        const Header & dst,
99
                                                        std::vector<std::string> & conflictingAttributes) const;
100
                                                                                                          
101
   TileOffsets*            createTileOffsets(const Header& header);
102
   
103
   InputPartData*          getPart(int partNumber);
104
   
105
    Data (bool deleteStream, int numThreads, bool reconstructChunkOffsetTable):
106
        InputStreamMutex(),
107
        deleteStream (deleteStream),
108
        numThreads (numThreads),
109
        reconstructChunkOffsetTable(reconstructChunkOffsetTable)
110
0
    {
111
0
    }
112
113
    ~Data()
114
0
    {
115
0
        if (deleteStream) delete is;
116
117
0
        for (size_t i = 0; i < parts.size(); i++)
118
0
            delete parts[i];
119
0
    }
120
    
121
    template <class T>
122
    T*    createInputPartT(int partNumber)
123
    {
124
125
    }
126
};
127
128
MultiPartInputFile::MultiPartInputFile(const char fileName[],
129
                           int numThreads,
130
                           bool reconstructChunkOffsetTable):
131
    _data(new Data(true, numThreads, reconstructChunkOffsetTable))
132
0
{
133
0
    try
134
0
    {
135
0
        _data->is = new StdIFStream (fileName);
136
0
        initialize();
137
0
    }
138
0
    catch (IEX_NAMESPACE::BaseExc &e)
139
0
    {
140
0
        delete _data;
141
142
0
        REPLACE_EXC (e, "Cannot read image file "
143
0
                        "\"" << fileName << "\". " << e);
144
0
        throw;
145
0
    }
146
0
    catch (...)
147
0
    {
148
0
        delete _data;
149
0
        throw;
150
0
    }
151
0
}
152
153
MultiPartInputFile::MultiPartInputFile (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is,
154
                                        int numThreads,
155
                                        bool reconstructChunkOffsetTable):
156
    _data(new Data(false, numThreads, reconstructChunkOffsetTable))
157
0
{
158
0
    try
159
0
    {
160
0
        _data->is = &is;
161
0
        initialize();
162
0
    }
163
0
    catch (IEX_NAMESPACE::BaseExc &e)
164
0
    {
165
0
        delete _data;
166
167
0
        REPLACE_EXC (e, "Cannot read image file "
168
0
                        "\"" << is.fileName() << "\". " << e);
169
0
        throw;
170
0
    }
171
0
    catch (...)
172
0
    {
173
0
        delete _data;
174
0
        throw;
175
0
    }
176
0
}
177
178
template<class T>
179
T*
180
MultiPartInputFile::getInputPart(int partNumber)
181
0
{
182
0
    Lock lock(*_data);
183
0
            if (_data->_inputFiles.find(partNumber) == _data->_inputFiles.end())
184
0
        {
185
0
            T* file = new T(_data->getPart(partNumber));
186
0
            _data->_inputFiles.insert(std::make_pair(partNumber, (GenericInputFile*) file));
187
0
            return file;
188
0
        }
189
0
        else return (T*) _data->_inputFiles[partNumber];
190
0
}
Unexecuted instantiation: Imf_2_2::InputFile* Imf_2_2::MultiPartInputFile::getInputPart<Imf_2_2::InputFile>(int)
Unexecuted instantiation: Imf_2_2::TiledInputFile* Imf_2_2::MultiPartInputFile::getInputPart<Imf_2_2::TiledInputFile>(int)
Unexecuted instantiation: Imf_2_2::DeepScanLineInputFile* Imf_2_2::MultiPartInputFile::getInputPart<Imf_2_2::DeepScanLineInputFile>(int)
Unexecuted instantiation: Imf_2_2::DeepTiledInputFile* Imf_2_2::MultiPartInputFile::getInputPart<Imf_2_2::DeepTiledInputFile>(int)
191
192
193
template InputFile* MultiPartInputFile::getInputPart<InputFile>(int);
194
template TiledInputFile* MultiPartInputFile::getInputPart<TiledInputFile>(int);
195
template DeepScanLineInputFile* MultiPartInputFile::getInputPart<DeepScanLineInputFile>(int);
196
template DeepTiledInputFile* MultiPartInputFile::getInputPart<DeepTiledInputFile>(int);
197
198
InputPartData*
199
MultiPartInputFile::getPart(int partNumber)
200
0
{
201
0
    return _data->getPart(partNumber);
202
0
}
203
204
205
206
const Header &
207
 MultiPartInputFile::header(int n) const
208
0
{
209
0
    return _data->_headers[n];
210
0
}
211
212
213
214
MultiPartInputFile::~MultiPartInputFile()
215
0
{
216
0
    for (map<int, GenericInputFile*>::iterator it = _data->_inputFiles.begin();
217
0
         it != _data->_inputFiles.end(); it++)
218
0
    {
219
0
        delete it->second;
220
0
    }
221
222
0
    delete _data;
223
0
}
224
225
226
bool
227
MultiPartInputFile::Data::checkSharedAttributesValues(const Header & src,
228
                                                const Header & dst,
229
                                                vector<string> & conflictingAttributes) const
230
0
{
231
0
    conflictingAttributes.clear();
232
233
0
    bool conflict = false;
234
235
    //
236
    // Display Window
237
    //
238
0
    if (src.displayWindow() != dst.displayWindow())
239
0
    {
240
0
        conflict = true;
241
0
        conflictingAttributes.push_back ("displayWindow");
242
0
    }
243
244
245
    //
246
    // Pixel Aspect Ratio
247
    //
248
0
    if (src.pixelAspectRatio() != dst.pixelAspectRatio())
249
0
    {
250
0
        conflict = true;
251
0
        conflictingAttributes.push_back ("pixelAspectRatio");
252
0
    }
253
254
255
    //
256
    // Timecode
257
    //
258
0
    const TimeCodeAttribute * srcTimeCode = src.findTypedAttribute<
259
0
          TimeCodeAttribute> (TimeCodeAttribute::staticTypeName());
260
0
    const TimeCodeAttribute * dstTimeCode = dst.findTypedAttribute<
261
0
          TimeCodeAttribute> (TimeCodeAttribute::staticTypeName());
262
263
0
    if (dstTimeCode)
264
0
    {
265
0
        if  ( (srcTimeCode && (srcTimeCode->value() != dstTimeCode->value())) ||
266
0
              (!srcTimeCode))
267
0
        {
268
0
            conflict = true;
269
0
            conflictingAttributes.push_back (TimeCodeAttribute::staticTypeName());
270
0
        }
271
0
    }
272
273
    //
274
    // Chromaticities
275
    //
276
0
    const ChromaticitiesAttribute * srcChrom =  src.findTypedAttribute<
277
0
          ChromaticitiesAttribute> (ChromaticitiesAttribute::staticTypeName());
278
0
    const ChromaticitiesAttribute * dstChrom =  dst.findTypedAttribute<
279
0
          ChromaticitiesAttribute> (ChromaticitiesAttribute::staticTypeName());
280
281
0
    if (dstChrom)
282
0
    {
283
0
        if ( (srcChrom && (srcChrom->value() != dstChrom->value())) ||
284
0
             (!srcChrom))
285
0
        {
286
0
            conflict = true;
287
0
            conflictingAttributes.push_back (ChromaticitiesAttribute::staticTypeName());
288
0
        }
289
0
    }
290
291
292
0
    return conflict;
293
0
}
294
295
296
void
297
MultiPartInputFile::initialize()
298
0
{
299
0
    readMagicNumberAndVersionField(*_data->is, _data->version);
300
    
301
0
    bool multipart = isMultiPart(_data->version);
302
0
    bool tiled = isTiled(_data->version);
303
304
    //
305
    // Multipart files don't have and shouldn't have the tiled bit set.
306
    //
307
308
0
    if (tiled && multipart)
309
0
        throw IEX_NAMESPACE::InputExc ("Multipart files cannot have the tiled bit set");
310
311
    
312
0
    int pos = 0;
313
0
    while (true)
314
0
    {
315
0
        Header header;
316
0
        header.readFrom(*_data->is, _data->version);
317
318
        //
319
        // If we read nothing then we stop reading.
320
        //
321
322
0
        if (header.readsNothing())
323
0
        {
324
0
            pos++;
325
0
            break;
326
0
        }
327
328
0
        _data->_headers.push_back(header);
329
        
330
0
        if(multipart == false)
331
0
          break;
332
0
    }
333
334
    //
335
    // Perform usual check on headers.
336
    //
337
338
0
    for (size_t i = 0; i < _data->_headers.size(); i++)
339
0
    {
340
        //
341
        // Silently invent a type if the file is a single part regular image.
342
        //
343
344
0
        if( _data->_headers[i].hasType() == false )
345
0
        {
346
0
            if(multipart)
347
348
0
                throw IEX_NAMESPACE::ArgExc ("Every header in a multipart file should have a type");
349
          
350
0
            _data->_headers[i].setType(tiled ? TILEDIMAGE : SCANLINEIMAGE);
351
0
        }
352
0
        else
353
0
        {
354
            
355
            //
356
            // Silently fix the header type if it's wrong
357
            // (happens when a regular Image file written by EXR_2.0 is rewritten by an older library,
358
            //  so doesn't effect deep image types)
359
            //
360
361
0
            if(!multipart && !isNonImage(_data->version))
362
0
            {
363
0
                _data->_headers[i].setType(tiled ? TILEDIMAGE : SCANLINEIMAGE);
364
0
            }
365
0
        }
366
         
367
368
        
369
0
        if( _data->_headers[i].hasName() == false )
370
0
        {
371
0
            if(multipart)
372
0
                throw IEX_NAMESPACE::ArgExc ("Every header in a multipart file should have a name");
373
0
        }
374
        
375
0
        if (isTiled(_data->_headers[i].type()))
376
0
            _data->_headers[i].sanityCheck(true, multipart);
377
0
        else
378
0
            _data->_headers[i].sanityCheck(false, multipart);
379
0
    }
380
381
    //
382
    // Check name uniqueness.
383
    //
384
385
0
    if (multipart)
386
0
    {
387
0
        set<string> names;
388
0
        for (size_t i = 0; i < _data->_headers.size(); i++)
389
0
        {
390
        
391
0
            if (names.find(_data->_headers[i].name()) != names.end())
392
0
            {
393
0
                throw IEX_NAMESPACE::InputExc ("Header name " + _data->_headers[i].name() +
394
0
                                   " is not a unique name.");
395
0
            }
396
0
            names.insert(_data->_headers[i].name());
397
0
        }
398
0
    }
399
    
400
    //
401
    // Check shared attributes compliance.
402
    //
403
404
0
    if (multipart && strictSharedAttribute)
405
0
    {
406
0
        for (size_t i = 1; i < _data->_headers.size(); i++)
407
0
        {
408
0
            vector <string> attrs;
409
0
            if (_data->checkSharedAttributesValues (_data->_headers[0], _data->_headers[i], attrs))
410
0
            {
411
0
                string attrNames;
412
0
                for (size_t j=0; j<attrs.size(); j++)
413
0
                    attrNames += " " + attrs[j];
414
0
                throw IEX_NAMESPACE::InputExc ("Header name " + _data->_headers[i].name() +
415
0
                                     " has non-conforming shared attributes: "+
416
0
                                     attrNames);
417
0
            }
418
0
        }
419
0
    }
420
421
    //
422
    // Create InputParts and read chunk offset tables.
423
    //
424
        
425
0
    for (size_t i = 0; i < _data->_headers.size(); i++)
426
0
        _data->parts.push_back(
427
0
                new InputPartData(_data, _data->_headers[i], i, _data->numThreads, _data->version));
428
429
0
    _data->readChunkOffsetTables(_data->reconstructChunkOffsetTable);
430
0
}
431
432
TileOffsets*
433
MultiPartInputFile::Data::createTileOffsets(const Header& header)
434
0
{
435
    //
436
    // Get the dataWindow information
437
    //
438
439
0
    const Box2i &dataWindow = header.dataWindow();
440
0
    int minX = dataWindow.min.x;
441
0
    int maxX = dataWindow.max.x;
442
0
    int minY = dataWindow.min.y;
443
0
    int maxY = dataWindow.max.y;
444
445
    //
446
    // Precompute level and tile information
447
    //
448
449
0
    int* numXTiles;
450
0
    int* numYTiles;
451
0
    int numXLevels, numYLevels;
452
0
    TileDescription tileDesc = header.tileDescription();
453
0
    precalculateTileInfo (tileDesc,
454
0
                          minX, maxX,
455
0
                          minY, maxY,
456
0
                          numXTiles, numYTiles,
457
0
                          numXLevels, numYLevels);
458
459
0
    TileOffsets* tileOffsets = new TileOffsets (tileDesc.mode,
460
0
                                                numXLevels,
461
0
                                                numYLevels,
462
0
                                                numXTiles,
463
0
                                                numYTiles);
464
0
    delete [] numXTiles;
465
0
    delete [] numYTiles;
466
467
0
    return tileOffsets;
468
0
}
469
470
471
void
472
MultiPartInputFile::Data::chunkOffsetReconstruction(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is, const vector<InputPartData*>& parts)
473
0
{
474
    //
475
    // Reconstruct broken chunk offset tables. Stop once we received any exception.
476
    //
477
478
0
    Int64 position = is.tellg();
479
480
    
481
    //
482
    // check we understand all the parts available: if not, we cannot continue
483
    // exceptions thrown here should trickle back up to the constructor
484
    //
485
    
486
0
    for (size_t i = 0; i < parts.size(); i++)
487
0
    {
488
0
        Header& header=parts[i]->header;
489
        
490
        //
491
        // do we have a valid type entry?
492
        // we only need them for true multipart files or single part non-image (deep) files
493
        //
494
0
        if(!header.hasType() && (isMultiPart(version) || isNonImage(version)))
495
0
        {
496
0
            throw IEX_NAMESPACE::ArgExc("cannot reconstruct incomplete file: part with missing type");
497
0
        }
498
0
        if(!isSupportedType(header.type()))
499
0
        {
500
0
            throw IEX_NAMESPACE::ArgExc("cannot reconstruct incomplete file: part with unknown type "+header.type());
501
0
        }
502
0
    }
503
    
504
    
505
    // how many chunks should we read? We should stop when we reach the end
506
0
    size_t total_chunks = 0;
507
        
508
    // for tiled-based parts, array of (pointers to) tileOffsets objects
509
    // to create mapping between tile coordinates and chunk table indices
510
    
511
    
512
0
    vector<TileOffsets*> tileOffsets(parts.size());
513
    
514
    // for scanline-based parts, number of scanlines in each part
515
0
    vector<int> rowsizes(parts.size());
516
        
517
0
    for(size_t i = 0 ; i < parts.size() ; i++)
518
0
    {
519
0
        total_chunks += parts[i]->chunkOffsets.size();
520
0
        if (isTiled(parts[i]->header.type()))
521
0
        {
522
0
            tileOffsets[i] = createTileOffsets(parts[i]->header);
523
0
        }else{
524
0
            tileOffsets[i] = NULL;
525
            // (TODO) fix this so that it doesn't need to be revised for future compression types.
526
0
            switch(parts[i]->header.compression())
527
0
            {
528
0
                case DWAB_COMPRESSION :
529
0
                    rowsizes[i] = 256;
530
0
                    break;
531
0
                case PIZ_COMPRESSION :
532
0
                case B44_COMPRESSION :
533
0
                case B44A_COMPRESSION :
534
0
                case DWAA_COMPRESSION :
535
0
                    rowsizes[i]=32;
536
0
                    break;
537
0
                case ZIP_COMPRESSION :
538
0
                case PXR24_COMPRESSION :
539
0
                    rowsizes[i]=16;
540
0
                    break;
541
0
                case ZIPS_COMPRESSION :
542
0
                case RLE_COMPRESSION :
543
0
                case NO_COMPRESSION :
544
0
                    rowsizes[i]=1;
545
0
                    break;
546
0
                default :
547
0
                    throw(IEX_NAMESPACE::ArgExc("Unknown compression method in chunk offset reconstruction"));
548
0
            }
549
0
        }
550
0
     }
551
        
552
0
     try
553
0
     {
554
            
555
        //
556
        // 
557
        //
558
        
559
0
        Int64 chunk_start = position;
560
0
        for (size_t i = 0; i < total_chunks ; i++)
561
0
        {
562
            //
563
            // do we have a part number?
564
            //
565
            
566
0
            int partNumber = 0;
567
0
            if(isMultiPart(version))
568
0
            {
569
0
                OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, partNumber);
570
0
            }
571
            
572
            
573
            
574
0
            if(partNumber<0 || partNumber>int(parts.size()))
575
0
            {
576
                // bail here - bad part number
577
0
                throw int();
578
0
            }
579
            
580
0
            Header& header = parts[partNumber]->header;
581
582
            // size of chunk NOT including multipart field
583
            
584
0
            Int64 size_of_chunk=0;
585
586
0
            if (isTiled(header.type()))
587
0
            {
588
                //
589
                // 
590
                //
591
0
                int tilex,tiley,levelx,levely;
592
0
                OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, tilex);
593
0
                OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, tiley);
594
0
                OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, levelx);
595
0
                OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, levely);
596
                
597
                //std::cout << "chunk_start for " << tilex <<',' << tiley << ',' << levelx << ' ' << levely << ':' << chunk_start << std::endl;
598
                    
599
                
600
0
                if(!tileOffsets[partNumber])
601
0
                {
602
                    // this shouldn't actually happen - we should have allocated a valid
603
                    // tileOffsets for any part which isTiled
604
0
                    throw int();
605
                    
606
0
                }
607
                
608
0
                if(!tileOffsets[partNumber]->isValidTile(tilex,tiley,levelx,levely))
609
0
                {
610
                    //std::cout << "invalid tile : aborting\n";
611
0
                    throw int();
612
0
                }
613
                
614
0
                (*tileOffsets[partNumber])(tilex,tiley,levelx,levely)=chunk_start;
615
                
616
                // compute chunk sizes - different procedure for deep tiles and regular
617
                // ones
618
0
                if(header.type()==DEEPTILE)
619
0
                {
620
0
                    Int64 packed_offset;
621
0
                    Int64 packed_sample;
622
0
                    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, packed_offset);
623
0
                    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, packed_sample);
624
                    
625
                    //add 40 byte header to packed sizes (tile coordinates, packed sizes, unpacked size)
626
0
                    size_of_chunk=packed_offset+packed_sample+40;
627
0
                }
628
0
                else
629
0
                {
630
                    
631
                    // regular image has 20 bytes of header, 4 byte chunksize;
632
0
                    int chunksize;
633
0
                    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, chunksize);
634
0
                    size_of_chunk=chunksize+20;
635
0
                }
636
0
            }
637
0
            else
638
0
            {
639
0
                int y_coordinate;
640
0
                OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, y_coordinate);
641
                
642
0
                y_coordinate -= header.dataWindow().min.y;
643
0
                y_coordinate /= rowsizes[partNumber];   
644
                
645
0
                if(y_coordinate < 0 || y_coordinate >= int(parts[partNumber]->chunkOffsets.size()))
646
0
                {
647
                    //std::cout << "aborting reconstruction: bad data " << y_coordinate << endl;
648
                    //bail to exception catcher: broken scanline
649
0
                    throw int();
650
0
                }
651
                
652
0
                parts[partNumber]->chunkOffsets[y_coordinate]=chunk_start;
653
                //std::cout << "chunk_start for " << y_coordinate << ':' << chunk_start << std::endl;
654
                
655
0
                if(header.type()==DEEPSCANLINE)
656
0
                {
657
0
                    Int64 packed_offset;
658
0
                    Int64 packed_sample;
659
0
                    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, packed_offset);
660
0
                    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, packed_sample);
661
                    
662
                    
663
0
                    size_of_chunk=packed_offset+packed_sample+28;
664
0
                }
665
0
                else
666
0
                {
667
0
                    int chunksize;
668
0
                    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, chunksize);   
669
0
                    size_of_chunk=chunksize+8;
670
0
                }
671
                
672
0
            }
673
            
674
0
            if(isMultiPart(version))
675
0
            {
676
0
                chunk_start+=4;
677
0
            }
678
            
679
0
            chunk_start+=size_of_chunk;
680
            
681
            //std::cout << " next chunk +"<<size_of_chunk << " = " << chunk_start << std::endl;
682
            
683
0
            is.seekg(chunk_start);
684
            
685
0
        }
686
        
687
0
    }
688
0
    catch (...)
689
0
    {
690
        //
691
        // Suppress all exceptions.  This functions is
692
        // called only to reconstruct the line offset
693
        // table for incomplete files, and exceptions
694
        // are likely.
695
        //
696
0
    }
697
698
    // copy tiled part data back to chunk offsets
699
    
700
0
    for(size_t partNumber=0;partNumber<parts.size();partNumber++)
701
0
    {
702
0
        if(tileOffsets[partNumber])
703
0
        {
704
0
            size_t pos=0;
705
0
            vector<vector<vector <Int64> > > offsets = tileOffsets[partNumber]->getOffsets();
706
0
            for (size_t l = 0; l < offsets.size(); l++)
707
0
                for (size_t y = 0; y < offsets[l].size(); y++)
708
0
                    for (size_t x = 0; x < offsets[l][y].size(); x++)
709
0
                    {
710
0
                        parts[ partNumber ]->chunkOffsets[pos] = offsets[l][y][x];
711
0
                        pos++;
712
0
                    }
713
0
           delete tileOffsets[partNumber];
714
0
        }
715
0
    }
716
717
0
    is.clear();
718
0
    is.seekg (position);
719
0
}
720
721
InputPartData*
722
MultiPartInputFile::Data::getPart(int partNumber)
723
0
{
724
0
    if (partNumber < 0 || partNumber >= (int) parts.size())
725
0
        throw IEX_NAMESPACE::ArgExc ("Part number is not in valid range.");
726
0
    return parts[partNumber];
727
0
}
728
729
730
731
void
732
MultiPartInputFile::Data::readChunkOffsetTables(bool reconstructChunkOffsetTable)
733
0
{
734
0
    bool brokenPartsExist = false;
735
736
0
    for (size_t i = 0; i < parts.size(); i++)
737
0
    {
738
0
        int chunkOffsetTableSize = getChunkOffsetTableSize(parts[i]->header,false);
739
0
        parts[i]->chunkOffsets.resize(chunkOffsetTableSize);
740
741
0
        for (int j = 0; j < chunkOffsetTableSize; j++)
742
0
            OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*is, parts[i]->chunkOffsets[j]);
743
744
        //
745
        // Check chunk offsets, reconstruct if broken.
746
        // At first we assume the table is complete.
747
        //
748
0
        parts[i]->completed = true;
749
0
        for (int j = 0; j < chunkOffsetTableSize; j++)
750
0
        {
751
0
            if (parts[i]->chunkOffsets[j] <= 0)
752
0
            {
753
0
                brokenPartsExist = true;
754
0
                parts[i]->completed = false;
755
0
                break;
756
0
            }
757
0
        }
758
0
    }
759
760
0
    if (brokenPartsExist && reconstructChunkOffsetTable)
761
0
        chunkOffsetReconstruction(*is, parts);
762
0
}
763
764
int 
765
MultiPartInputFile::version() const
766
0
{
767
0
    return _data->version;
768
0
}
769
770
bool 
771
MultiPartInputFile::partComplete(int part) const
772
0
{
773
0
  return _data->parts[part]->completed;
774
0
}
775
776
int 
777
MultiPartInputFile::parts() const
778
0
{
779
0
   return int(_data->_headers.size());
780
0
}
781
782
783
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT