Coverage Report

Created: 2025-07-16 07:53

/src/openexr/src/lib/OpenEXR/ImfTiledInputFile.cpp
Line
Count
Source (jump to first uncovered line)
1
//
2
// SPDX-License-Identifier: BSD-3-Clause
3
// Copyright (c) Contributors to the OpenEXR Project.
4
//
5
6
//-----------------------------------------------------------------------------
7
//
8
//  class TiledInputFile
9
//
10
//-----------------------------------------------------------------------------
11
12
#include "ImfTiledInputFile.h"
13
14
#include "Iex.h"
15
16
#include "IlmThreadPool.h"
17
#if ILMTHREAD_THREADING_ENABLED
18
#    include "IlmThreadProcessGroup.h"
19
#    include <mutex>
20
#endif
21
22
#include "ImfFrameBuffer.h"
23
#include "ImfInputPartData.h"
24
25
// TODO: remove once TiledOutput is converted
26
#include "ImfTileOffsets.h"
27
#include "ImfTiledMisc.h"
28
29
#include <algorithm>
30
#include <vector>
31
32
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
33
34
namespace {
35
36
struct TileProcess
37
{
38
    ~TileProcess ()
39
608
    {
40
608
        if (!first)
41
120
            exr_decoding_destroy (decoder.context, &decoder);
42
608
    }
43
44
    void run_decode (
45
        exr_const_context_t ctxt,
46
        int pn,
47
        const FrameBuffer *outfb,
48
        const std::vector<Slice> &filllist);
49
50
    void update_pointers (
51
        const FrameBuffer *outfb,
52
        int fb_absX, int fb_absY,
53
        int t_absX, int t_absY);
54
55
    void run_fill (
56
        const FrameBuffer *outfb,
57
        int fb_absX, int fb_absY,
58
        int t_absX, int t_absY,
59
        const std::vector<Slice> &filllist);
60
61
    bool                  first = true;
62
    exr_chunk_info_t      cinfo;
63
    exr_decode_pipeline_t decoder;
64
65
    TileProcess*          next;
66
};
67
68
#if ILMTHREAD_THREADING_ENABLED
69
using TileProcessGroup = ILMTHREAD_NAMESPACE::ProcessGroup<TileProcess>;
70
#endif
71
72
} // empty namespace
73
74
//
75
// struct TiledInputFile::Data stores things that will be
76
// needed between calls to readTile()
77
//
78
79
struct TiledInputFile::Data
80
{
81
    Data (Context *ctxt, int pN, int nT)
82
649
    : _ctxt (ctxt)
83
649
    , partNumber (pN)
84
649
    , numThreads (nT)
85
649
    {}
86
87
    void initialize ()
88
649
    {
89
649
        if (_ctxt->storage (partNumber) != EXR_STORAGE_TILED)
90
1
            throw IEX_NAMESPACE::ArgExc ("File part is not a tiled part");
91
92
648
        if (EXR_ERR_SUCCESS != exr_get_tile_descriptor (
93
648
                *_ctxt,
94
648
                partNumber,
95
648
                &tile_x_size,
96
648
                &tile_y_size,
97
648
                &tile_level_mode,
98
648
                &tile_round_mode))
99
0
            throw IEX_NAMESPACE::ArgExc ("Unable to query tile descriptor");
100
101
648
        if (EXR_ERR_SUCCESS != exr_get_tile_levels (
102
648
                *_ctxt,
103
648
                partNumber,
104
648
                &num_x_levels,
105
648
                &num_y_levels))
106
0
            throw IEX_NAMESPACE::ArgExc ("Unable to query number of tile levels");
107
648
    }
108
109
    void readTiles (int dx1, int dx2, int dy1, int dy2, int lx, int ly);
110
111
    Context* _ctxt;
112
    int partNumber;
113
    int numThreads;
114
    Header header;
115
    bool header_filled = false;
116
117
    uint32_t tile_x_size = 0;
118
    uint32_t tile_y_size = 0;
119
    exr_tile_level_mode_t tile_level_mode = EXR_TILE_LAST_TYPE;
120
    exr_tile_round_mode_t tile_round_mode = EXR_TILE_ROUND_LAST_TYPE;
121
122
    int32_t num_x_levels = 0;
123
    int32_t num_y_levels = 0;
124
125
    // TODO: remove once we can remove deprecated API
126
    std::vector<char> _tile_data_scratch;
127
128
    FrameBuffer frameBuffer;
129
    std::vector<Slice> fill_list;
130
131
    std::vector<std::string> _failures;
132
133
#if ILMTHREAD_THREADING_ENABLED
134
    std::mutex _mx;
135
136
    class TileBufferTask final : public ILMTHREAD_NAMESPACE::Task
137
    {
138
    public:
139
        TileBufferTask (
140
            ILMTHREAD_NAMESPACE::TaskGroup* group,
141
            Data*                   ifd,
142
            TileProcessGroup*       tileg,
143
            const FrameBuffer*      outfb,
144
            const exr_chunk_info_t& cinfo)
145
2
            : Task (group)
146
2
            , _outfb (outfb)
147
2
            , _ifd (ifd)
148
2
            , _tile (tileg->pop ())
149
2
            , _tile_group (tileg)
150
2
        {
151
2
            _tile->cinfo = cinfo;
152
2
        }
153
154
        ~TileBufferTask () override
155
2
        {
156
2
            _tile_group->push (_tile);
157
2
        }
158
159
        void execute () override;
160
161
    private:
162
        void run_decode ();
163
164
        const FrameBuffer* _outfb;
165
        Data*              _ifd;
166
167
        TileProcess*       _tile;
168
        TileProcessGroup*  _tile_group;
169
    };
170
#endif
171
};
172
173
TiledInputFile::TiledInputFile (
174
    const char*               filename,
175
    const ContextInitializer& ctxtinit,
176
    int                       numThreads)
177
0
    : _ctxt (filename, ctxtinit, Context::read_mode_t{})
178
0
    , _data (std::make_shared<Data> (&_ctxt, 0, numThreads))
179
0
{
180
0
    _data->initialize ();
181
0
}
182
183
TiledInputFile::TiledInputFile (const char fileName[], int numThreads)
184
0
    : TiledInputFile (
185
0
        fileName,
186
0
        ContextInitializer ()
187
0
        .silentHeaderParse (true)
188
0
        .strictHeaderValidation (false),
189
0
        numThreads)
190
0
{
191
0
}
192
193
TiledInputFile::TiledInputFile (
194
    OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is, int numThreads)
195
0
    : TiledInputFile (
196
0
        is.fileName (),
197
0
        ContextInitializer ()
198
0
        .silentHeaderParse (true)
199
0
        .strictHeaderValidation (false)
200
0
        .setInputStream (&is),
201
0
        numThreads)
202
0
{
203
0
}
204
205
TiledInputFile::TiledInputFile (InputPartData* part)
206
649
    : _ctxt (part->context),
207
649
      _data (std::make_shared<Data> (&_ctxt, part->partNumber, part->numThreads))
208
649
{
209
649
    _data->initialize ();
210
649
}
211
212
const char*
213
TiledInputFile::fileName () const
214
222
{
215
222
    return _ctxt.fileName ();
216
222
}
217
218
const Header&
219
TiledInputFile::header () const
220
0
{
221
0
#if ILMTHREAD_THREADING_ENABLED
222
0
    std::lock_guard<std::mutex> lock (_data->_mx);
223
0
#endif
224
0
    if (!_data->header_filled)
225
0
    {
226
0
        _data->header = _ctxt.header (_data->partNumber);
227
0
        _data->header_filled = true;
228
0
    }
229
0
    return _data->header;
230
0
}
231
232
int
233
TiledInputFile::version () const
234
0
{
235
0
    return _ctxt.version ();
236
0
}
237
238
void
239
TiledInputFile::setFrameBuffer (const FrameBuffer& frameBuffer)
240
12.2k
{
241
12.2k
#if ILMTHREAD_THREADING_ENABLED
242
12.2k
    std::lock_guard<std::mutex> lock (_data->_mx);
243
12.2k
#endif
244
12.2k
    _data->fill_list.clear ();
245
246
12.2k
    for (FrameBuffer::ConstIterator j = frameBuffer.begin ();
247
22.1k
         j != frameBuffer.end ();
248
12.2k
         ++j)
249
9.93k
    {
250
9.93k
        const exr_attr_chlist_entry_t* curc = _ctxt.findChannel (
251
9.93k
            _data->partNumber, j.name ());
252
253
9.93k
        if (!curc)
254
611
        {
255
611
            _data->fill_list.push_back (j.slice ());
256
611
            continue;
257
611
        }
258
259
9.31k
        if (curc->x_sampling != j.slice ().xSampling ||
260
9.31k
            curc->y_sampling != j.slice ().ySampling)
261
1
            THROW (
262
9.31k
                IEX_NAMESPACE::ArgExc,
263
9.31k
                "X and/or y subsampling factors "
264
9.31k
                "of \""
265
9.31k
                    << j.name ()
266
9.31k
                    << "\" channel "
267
9.31k
                       "of input file \""
268
9.31k
                    << fileName ()
269
9.31k
                    << "\" are "
270
9.31k
                       "not compatible with the frame buffer's "
271
9.31k
                       "subsampling factors.");
272
9.31k
    }
273
274
12.2k
    _data->frameBuffer = frameBuffer;
275
12.2k
}
276
277
const FrameBuffer&
278
TiledInputFile::frameBuffer () const
279
0
{
280
0
#if ILMTHREAD_THREADING_ENABLED
281
0
    std::lock_guard<std::mutex> lock (_data->_mx);
282
0
#endif
283
0
    return _data->frameBuffer;
284
0
}
285
286
bool
287
TiledInputFile::isComplete () const
288
0
{
289
0
    return _ctxt.chunkTableValid (_data->partNumber);
290
0
}
291
292
void
293
TiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int lx, int ly)
294
293
{
295
    //
296
    // Read a range of tiles from the file into the framebuffer
297
    //
298
299
293
    try
300
293
    {
301
293
        if (!isValidLevel (lx, ly))
302
0
            THROW (
303
293
                IEX_NAMESPACE::ArgExc,
304
293
                "Level coordinate "
305
293
                "(" << lx
306
293
                    << ", " << ly
307
293
                    << ") "
308
293
                       "is invalid.");
309
310
293
        if (dx1 > dx2) std::swap (dx1, dx2);
311
293
        if (dy1 > dy2) std::swap (dy1, dy2);
312
313
293
        _data->readTiles (dx1, dx2, dy1, dy2, lx, ly);
314
293
    }
315
293
    catch (IEX_NAMESPACE::BaseExc& e)
316
293
    {
317
221
        REPLACE_EXC (
318
221
            e,
319
221
            "Error reading pixel data from image "
320
221
            "file \""
321
221
                << fileName () << "\". " << e.what ());
322
221
        throw;
323
221
    }
324
293
}
325
326
void
327
TiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int l)
328
0
{
329
0
    readTiles (dx1, dx2, dy1, dy2, l, l);
330
0
}
331
332
void
333
TiledInputFile::readTile (int dx, int dy, int lx, int ly)
334
0
{
335
0
    readTiles (dx, dx, dy, dy, lx, ly);
336
0
}
337
338
void
339
TiledInputFile::readTile (int dx, int dy, int l)
340
0
{
341
0
    readTile (dx, dy, l, l);
342
0
}
343
344
void
345
TiledInputFile::rawTileData (
346
    int&         dx,
347
    int&         dy,
348
    int&         lx,
349
    int&         ly,
350
    const char*& pixelData,
351
    int&         pixelDataSize)
352
0
{
353
0
    exr_chunk_info_t cinfo;
354
0
    if (EXR_ERR_SUCCESS == exr_read_tile_chunk_info (
355
0
            _ctxt, _data->partNumber, dx, dy, lx, ly, &cinfo))
356
0
    {
357
0
#if ILMTHREAD_THREADING_ENABLED
358
0
        std::lock_guard<std::mutex> lock (_data->_mx);
359
0
#endif
360
0
        _data->_tile_data_scratch.resize (cinfo.packed_size);
361
0
        pixelDataSize = static_cast<int> (cinfo.packed_size);
362
0
        if (EXR_ERR_SUCCESS !=
363
0
            exr_read_chunk (_ctxt, _data->partNumber, &cinfo,
364
0
                            _data->_tile_data_scratch.data ()))
365
0
        {
366
0
            THROW (
367
0
                IEX_NAMESPACE::ArgExc,
368
0
                "Error reading pixel data from image "
369
0
                "file \""
370
0
                    << fileName () << "\". Unable to read raw tile data of "
371
0
                    << pixelDataSize << " bytes.");
372
0
        }
373
0
        pixelData = _data->_tile_data_scratch.data ();
374
0
        dx = cinfo.start_x;
375
0
        dy = cinfo.start_y;
376
0
        lx = cinfo.level_x;
377
0
        ly = cinfo.level_y;
378
0
    }
379
0
    else
380
0
    {
381
0
        if (!isValidTile (dx, dy, lx, ly))
382
0
        {
383
0
            THROW (
384
0
                IEX_NAMESPACE::ArgExc,
385
0
                "Error reading pixel data from image "
386
0
                "file \""
387
0
                << fileName () << "\". "
388
0
                << "Tried to read a tile outside "
389
0
                "the image file's data window.");
390
0
        }
391
0
        else
392
0
        {
393
0
            THROW (
394
0
                IEX_NAMESPACE::ArgExc,
395
0
                "Error reading chunk information for tile from image "
396
0
                "file \""
397
0
                << fileName () << "\". Unable to read raw tile offset information.");
398
0
        }
399
0
    }
400
0
}
401
402
unsigned int
403
TiledInputFile::tileXSize () const
404
0
{
405
0
    return _data->tile_x_size;
406
0
}
407
408
unsigned int
409
TiledInputFile::tileYSize () const
410
25.0k
{
411
25.0k
    return _data->tile_y_size;
412
25.0k
}
413
414
LevelMode
415
TiledInputFile::levelMode () const
416
293
{
417
293
    return (LevelMode)_data->tile_level_mode;
418
293
}
419
420
LevelRoundingMode
421
TiledInputFile::levelRoundingMode () const
422
0
{
423
0
    return (LevelRoundingMode)_data->tile_round_mode;
424
0
}
425
426
int
427
TiledInputFile::numLevels () const
428
0
{
429
0
    if (levelMode () == RIPMAP_LEVELS)
430
0
        THROW (
431
0
            IEX_NAMESPACE::LogicExc,
432
0
            "Error calling numLevels() on image "
433
0
            "file \""
434
0
                << fileName ()
435
0
                << "\" "
436
0
                   "(numLevels() is not defined for files "
437
0
                   "with RIPMAP level mode).");
438
439
0
    return _data->num_x_levels;
440
0
}
441
442
int
443
TiledInputFile::numXLevels () const
444
293
{
445
293
    return _data->num_x_levels;
446
293
}
447
448
int
449
TiledInputFile::numYLevels () const
450
293
{
451
293
    return _data->num_y_levels;
452
293
}
453
454
bool
455
TiledInputFile::isValidLevel (int lx, int ly) const
456
293
{
457
293
    if (lx < 0 || ly < 0) return false;
458
459
293
    if (levelMode () == MIPMAP_LEVELS && lx != ly) return false;
460
461
293
    if (lx >= numXLevels () || ly >= numYLevels ()) return false;
462
463
293
    return true;
464
293
}
465
466
int
467
TiledInputFile::levelWidth (int lx) const
468
315
{
469
315
    int32_t levw = 0;
470
315
    if (EXR_ERR_SUCCESS != exr_get_level_sizes (
471
315
            _ctxt, _data->partNumber, lx, 0, &levw, nullptr))
472
0
    {
473
0
        THROW (
474
0
            IEX_NAMESPACE::ArgExc,
475
0
            "Error calling levelWidth() on image "
476
0
            "file \""
477
0
                << fileName () << "\".");
478
0
    }
479
315
    return levw;
480
315
}
481
482
int
483
TiledInputFile::levelHeight (int ly) const
484
0
{
485
0
    int32_t levh = 0;
486
0
    if (EXR_ERR_SUCCESS != exr_get_level_sizes (
487
0
            _ctxt, _data->partNumber, 0, ly, nullptr, &levh))
488
0
    {
489
0
        THROW (
490
0
            IEX_NAMESPACE::ArgExc,
491
0
            "Error calling levelWidth() on image "
492
0
            "file \""
493
0
                << fileName () << "\".");
494
0
    }
495
0
    return levh;
496
0
}
497
498
int
499
TiledInputFile::numXTiles (int lx) const
500
293
{
501
293
    int32_t countx = 0;
502
293
    if (EXR_ERR_SUCCESS != exr_get_tile_counts (
503
293
            _ctxt, _data->partNumber, lx, 0, &countx, nullptr))
504
0
    {
505
0
        THROW (
506
0
            IEX_NAMESPACE::ArgExc,
507
0
            "Error calling numXTiles() on image "
508
0
            "file \""
509
0
                << fileName () << "\".");
510
0
    }
511
293
    return countx;
512
293
}
513
514
int
515
TiledInputFile::numYTiles (int ly) const
516
0
{
517
0
    int32_t county = 0;
518
0
    if (EXR_ERR_SUCCESS != exr_get_tile_counts (
519
0
            _ctxt, _data->partNumber, 0, ly, nullptr, &county))
520
0
    {
521
0
        THROW (
522
0
            IEX_NAMESPACE::ArgExc,
523
0
            "Error calling numYTiles() on image "
524
0
            "file \""
525
0
                << fileName () << "\".");
526
0
    }
527
0
    return county;
528
0
}
529
530
IMATH_NAMESPACE::Box2i
531
TiledInputFile::dataWindowForLevel (int l) const
532
0
{
533
0
    return dataWindowForLevel (l, l);
534
0
}
535
536
IMATH_NAMESPACE::Box2i
537
TiledInputFile::dataWindowForLevel (int lx, int ly) const
538
12.0k
{
539
12.0k
    int32_t levw = 0, levh = 0;
540
12.0k
    if (EXR_ERR_SUCCESS != exr_get_level_sizes (
541
12.0k
            _ctxt, _data->partNumber, lx, ly, &levw, &levh))
542
0
    {
543
0
        THROW (
544
0
            IEX_NAMESPACE::ArgExc,
545
0
            "Error calling dataWindowForLevel() on image "
546
0
            "file \""
547
0
                << fileName () << "\".");
548
0
    }
549
12.0k
    exr_attr_box2i_t dw = _ctxt.dataWindow (_data->partNumber);
550
12.0k
    return IMATH_NAMESPACE::Box2i (
551
12.0k
        IMATH_NAMESPACE::V2i (dw.min.x, dw.min.y),
552
12.0k
        IMATH_NAMESPACE::V2i (dw.min.x + levw - 1, dw.min.y + levh - 1));
553
12.0k
}
554
555
IMATH_NAMESPACE::Box2i
556
TiledInputFile::dataWindowForTile (int dx, int dy, int l) const
557
12.0k
{
558
12.0k
    return dataWindowForTile (dx, dy, l, l);
559
12.0k
}
560
561
IMATH_NAMESPACE::Box2i
562
TiledInputFile::dataWindowForTile (int dx, int dy, int lx, int ly) const
563
12.0k
{
564
12.0k
    try
565
12.0k
    {
566
12.0k
        if (!isValidTile (dx, dy, lx, ly))
567
0
            throw IEX_NAMESPACE::ArgExc ("Arguments not in valid range.");
568
569
        //exr_attr_box2i_t dw = _ctxt.dataWindow (_data->partNumber);
570
12.0k
        auto dw = dataWindowForLevel (lx, ly);
571
572
12.0k
        int32_t tileSizeX, tileSizeY;
573
12.0k
        if (EXR_ERR_SUCCESS !=
574
12.0k
            exr_get_tile_sizes (_ctxt, _data->partNumber, lx, ly, &tileSizeX, &tileSizeY))
575
0
          throw IEX_NAMESPACE::ArgExc ("Unable to query the data window.");
576
577
12.0k
        dw.min.x += dx * tileSizeX;
578
12.0k
        dw.min.y += dy * tileSizeY;
579
12.0k
        int limX = dw.min.x + tileSizeX - 1;
580
12.0k
        int limY = dw.min.y + tileSizeY - 1;
581
12.0k
        limX = std::min (limX, dw.max.x);
582
12.0k
        limY = std::min (limY, dw.max.y);
583
584
12.0k
        return IMATH_NAMESPACE::Box2i (
585
12.0k
            IMATH_NAMESPACE::V2i (dw.min.x, dw.min.y),
586
12.0k
            IMATH_NAMESPACE::V2i (limX, limY));
587
12.0k
    }
588
12.0k
    catch (IEX_NAMESPACE::BaseExc& e)
589
12.0k
    {
590
0
        REPLACE_EXC (
591
0
            e,
592
0
            "Error calling dataWindowForTile() on image "
593
0
            "file \""
594
0
                << fileName () << "\". " << e.what ());
595
0
        throw;
596
0
    }
597
12.0k
}
598
599
bool
600
TiledInputFile::isValidTile (int dx, int dy, int lx, int ly) const
601
12.0k
{
602
12.0k
    int32_t countx = 0, county = 0;
603
12.0k
    if (EXR_ERR_SUCCESS == exr_get_tile_counts (
604
12.0k
            _ctxt, _data->partNumber, lx, ly, &countx, &county))
605
12.0k
    {
606
        // get tile counts will check lx, ly for us
607
12.0k
        return ((dx < countx && dx >= 0) &&
608
12.0k
                (dy < county && dy >= 0));
609
12.0k
    }
610
0
    return false;
611
12.0k
}
612
613
namespace
614
{
615
struct tilepos
616
{
617
    uint64_t filePos;
618
    int      dx;
619
    int      dy;
620
    int      lx;
621
    int      ly;
622
    bool     operator< (const tilepos& other) const
623
0
    {
624
0
        return filePos < other.filePos;
625
0
    }
626
};
627
} // namespace
628
629
void
630
TiledInputFile::tileOrder (int dx[], int dy[], int lx[], int ly[]) const
631
0
{
632
    // TODO: remove once TiledOutputFile copy is converted
633
0
    switch (_ctxt.lineOrder (_data->partNumber))
634
0
    {
635
0
        case EXR_LINEORDER_RANDOM_Y:
636
            // calc below outside the nest
637
0
            break;
638
639
0
        case EXR_LINEORDER_DECREASING_Y:
640
0
        {
641
0
            dx[0] = 0;
642
0
            dy[0] = numYTiles (0) - 1;
643
0
            lx[0] = 0;
644
0
            ly[0] = 0;
645
0
            return;
646
0
        }
647
0
        case EXR_LINEORDER_INCREASING_Y:
648
0
            dx[0] = 0;
649
0
            dy[0] = 0;
650
0
            lx[0] = 0;
651
0
            ly[0] = 0;
652
0
            return;
653
654
0
        case EXR_LINEORDER_LAST_TYPE: /* invalid but should never be here */
655
0
        default:
656
0
            throw IEX_NAMESPACE::ArgExc ("Unknown LineOrder.");
657
0
    }
658
659
0
    size_t numAllTiles = 0;
660
0
    int numX = numXLevels ();
661
0
    int numY = numYLevels ();
662
663
0
    switch (levelMode ())
664
0
    {
665
0
        case ONE_LEVEL:
666
0
        case MIPMAP_LEVELS:
667
0
            for (int i_l = 0; i_l < numY; ++i_l)
668
0
                numAllTiles += size_t (numXTiles (i_l)) * size_t (numYTiles (i_l));
669
0
            break;
670
671
0
        case RIPMAP_LEVELS:
672
0
            for (int i_ly = 0; i_ly < numY; ++i_ly)
673
0
                for (int i_lx = 0; i_lx < numX; ++i_lx)
674
0
                    numAllTiles += size_t (numXTiles (i_lx)) * size_t (numYTiles (i_ly));
675
0
            break;
676
677
0
        default:
678
0
            throw IEX_NAMESPACE::ArgExc ("Unknown LevelMode format.");
679
0
    }
680
681
0
    std::vector<tilepos> table;
682
683
0
    table.resize (numAllTiles);
684
0
    size_t tIdx = 0;
685
0
    switch (levelMode ())
686
0
    {
687
0
        case ONE_LEVEL:
688
0
        case MIPMAP_LEVELS:
689
0
            for (int i_l = 0; i_l < numY; ++i_l)
690
0
            {
691
0
                int nY = numYTiles (i_l);
692
0
                int nX = numXTiles (i_l);
693
694
0
                for ( int y = 0; y < nY; ++y )
695
0
                    for ( int x = 0; x < nX; ++x )
696
0
                    {
697
0
                        exr_chunk_info_t cinfo;
698
0
                        if (EXR_ERR_SUCCESS == exr_read_tile_chunk_info (
699
0
                                _ctxt, _data->partNumber, x, y, i_l, i_l, &cinfo))
700
0
                        {
701
0
                            tilepos &tp = table[tIdx++];
702
0
                            tp.filePos = cinfo.data_offset;
703
0
                            tp.dx = x;
704
0
                            tp.dy = y;
705
0
                            tp.lx = i_l;
706
0
                            tp.ly = i_l;
707
0
                        }
708
0
                        else
709
0
                        {
710
0
                            throw IEX_NAMESPACE::ArgExc ("Unable to get tile offset.");
711
0
                        }
712
0
                    }
713
0
            }
714
0
            break;
715
716
0
        case RIPMAP_LEVELS:
717
0
            for (int i_ly = 0; i_ly < numY; ++i_ly)
718
0
            {
719
0
                int nY = numYTiles (i_ly);
720
0
                for (int i_lx = 0; i_lx < numX; ++i_lx)
721
0
                {
722
0
                    int nX = numXTiles (i_lx);
723
0
                    for ( int y = 0; y < nY; ++y )
724
0
                        for ( int x = 0; x < nX; ++x )
725
0
                        {
726
0
                            exr_chunk_info_t cinfo;
727
0
                            if (EXR_ERR_SUCCESS == exr_read_tile_chunk_info (
728
0
                                    _ctxt, _data->partNumber, x, y, i_lx, i_ly, &cinfo))
729
0
                            {
730
0
                                tilepos &tp = table[tIdx++];
731
0
                                tp.filePos = cinfo.data_offset;
732
0
                                tp.dx = x;
733
0
                                tp.dy = y;
734
0
                                tp.lx = i_lx;
735
0
                                tp.ly = i_ly;
736
0
                            }
737
0
                            else
738
0
                            {
739
0
                                throw IEX_NAMESPACE::ArgExc ("Unable to get tile offset.");
740
0
                            }
741
0
                        }
742
0
                }
743
0
            }
744
0
            break;
745
746
0
        default: throw IEX_NAMESPACE::ArgExc ("Unknown LevelMode format.");
747
0
    }
748
749
0
    std::sort (table.begin(), table.end ());
750
751
0
    for (size_t i = 0; i < numAllTiles; ++i)
752
0
    {
753
0
        const auto& tp = table[i];
754
0
        dx[i] = tp.dx;
755
0
        dy[i] = tp.dy;
756
0
        lx[i] = tp.lx;
757
0
        ly[i] = tp.ly;
758
0
    }
759
0
}
760
761
void TiledInputFile::Data::readTiles (int dx1, int dx2, int dy1, int dy2, int lx, int ly)
762
293
{
763
293
    int nTiles = dx2 - dx1 + 1;
764
293
    nTiles *= dy2 - dy1 + 1;
765
766
293
    exr_chunk_info_t      cinfo;
767
293
#if ILMTHREAD_THREADING_ENABLED
768
293
    if (nTiles > 1 && numThreads > 1)
769
21
    {
770
        // we need the lifetime of this to last longer than the
771
        // lifetime of the task group below such that we don't get use
772
        // after free type error, so use scope rules to accomplish
773
        // this
774
21
        TileProcessGroup tpg (numThreads);
775
776
21
        {
777
21
            ILMTHREAD_NAMESPACE::TaskGroup tg;
778
779
23
            for (int ty = dy1; ty <= dy2; ++ty)
780
23
            {
781
25
                for (int tx = dx1; tx <= dx2; ++tx)
782
23
                {
783
23
                    exr_result_t rv = exr_read_tile_chunk_info (
784
23
                        *_ctxt, partNumber, tx, ty, lx, ly, &cinfo);
785
23
                    if (EXR_ERR_INCOMPLETE_CHUNK_TABLE == rv)
786
0
                    {
787
0
                        THROW (
788
0
                            IEX_NAMESPACE::InputExc,
789
0
                            "Tile (" << tx << ", " << ty << ", " << lx << ", " << ly
790
0
                            << ") is missing.");
791
0
                    }
792
23
                    else if (EXR_ERR_SUCCESS != rv)
793
21
                        throw IEX_NAMESPACE::InputExc ("Unable to query tile information");
794
795
2
                    ILMTHREAD_NAMESPACE::ThreadPool::addGlobalTask (
796
2
                        new TileBufferTask (&tg, this, &tpg, &frameBuffer, cinfo) );
797
2
                }
798
23
            }
799
21
        }
800
801
0
        tpg.throw_on_failure ();
802
0
    }
803
272
    else
804
272
#endif
805
272
    {
806
272
        TileProcess tp;
807
808
390
        for (int ty = dy1; ty <= dy2; ++ty)
809
272
        {
810
390
            for (int tx = dx1; tx <= dx2; ++tx)
811
272
            {
812
272
                exr_result_t rv = exr_read_tile_chunk_info (
813
272
                    *_ctxt, partNumber, tx, ty, lx, ly, &cinfo);
814
272
                if (EXR_ERR_INCOMPLETE_CHUNK_TABLE == rv)
815
0
                {
816
0
                    THROW (
817
0
                        IEX_NAMESPACE::InputExc,
818
0
                        "Tile (" << tx << ", " << ty << ", " << lx << ", " << ly
819
0
                        << ") is missing.");
820
0
                }
821
272
                else if (EXR_ERR_SUCCESS != rv)
822
154
                    throw IEX_NAMESPACE::InputExc ("Unable to query tile information");
823
824
118
                tp.cinfo = cinfo;
825
118
                tp.run_decode (
826
118
                    *_ctxt,
827
118
                    partNumber,
828
118
                    &frameBuffer,
829
118
                    fill_list);
830
118
            }
831
272
        }
832
272
    }
833
293
}
834
835
////////////////////////////////////////
836
837
#if ILMTHREAD_THREADING_ENABLED
838
void TiledInputFile::Data::TileBufferTask::execute ()
839
2
{
840
2
    try
841
2
    {
842
2
        _tile->run_decode (
843
2
            *(_ifd->_ctxt),
844
2
            _ifd->partNumber,
845
2
            _outfb,
846
2
            _ifd->fill_list);
847
2
    }
848
2
    catch (std::exception &e)
849
2
    {
850
0
        _tile_group->record_failure (e.what ());
851
0
    }
852
2
    catch (...)
853
2
    {
854
0
        _tile_group->record_failure ("Unknown exception");
855
0
    }
856
2
}
857
#endif
858
859
////////////////////////////////////////
860
861
void TileProcess::run_decode (
862
    exr_const_context_t ctxt,
863
    int pn,
864
    const FrameBuffer *outfb,
865
    const std::vector<Slice> &filllist)
866
120
{
867
120
    int absX, absY, tileX, tileY;
868
120
    exr_attr_box2i_t dw;
869
870
    // stash the flag off to make sure to clean up in the event
871
    // of an exception by changing the flag after init...
872
120
    bool isfirst = first;
873
120
    if (first)
874
120
    {
875
120
        if (EXR_ERR_SUCCESS !=
876
120
            exr_decoding_initialize (ctxt, pn, &cinfo, &decoder))
877
0
        {
878
0
            throw IEX_NAMESPACE::IoExc ("Unable to initialize decode pipeline");
879
0
        }
880
881
120
        first = false;
882
120
    }
883
0
    else
884
0
    {
885
0
        if (EXR_ERR_SUCCESS !=
886
0
            exr_decoding_update (ctxt, pn, &cinfo, &decoder))
887
0
        {
888
0
            throw IEX_NAMESPACE::IoExc ("Unable to update decode pipeline");
889
0
        }
890
0
    }
891
892
120
    if (EXR_ERR_SUCCESS != exr_get_data_window (ctxt, pn, &dw))
893
0
        throw IEX_NAMESPACE::ArgExc ("Unable to query the data window.");
894
895
120
    if (EXR_ERR_SUCCESS != exr_get_tile_sizes (
896
120
            ctxt, pn, cinfo.level_x, cinfo.level_y, &tileX, &tileY))
897
0
        throw IEX_NAMESPACE::ArgExc ("Unable to query the data window.");
898
899
120
    absX = dw.min.x + tileX * cinfo.start_x;
900
120
    absY = dw.min.y + tileY * cinfo.start_y;
901
902
120
    update_pointers (outfb, dw.min.x, dw.min.y, absX, absY);
903
904
120
    if (isfirst)
905
120
    {
906
120
        if (EXR_ERR_SUCCESS !=
907
120
            exr_decoding_choose_default_routines (ctxt, pn, &decoder))
908
0
        {
909
0
            throw IEX_NAMESPACE::IoExc ("Unable to choose decoder routines");
910
0
        }
911
120
    }
912
913
120
    if (EXR_ERR_SUCCESS != exr_decoding_run (ctxt, pn, &decoder))
914
45
        throw IEX_NAMESPACE::IoExc ("Unable to run decoder");
915
916
75
    run_fill (outfb, dw.min.x, dw.min.y, absX, absY, filllist);
917
75
}
918
919
////////////////////////////////////////
920
921
void TileProcess::update_pointers (const FrameBuffer *outfb, int fb_absX, int fb_absY, int t_absX, int t_absY)
922
120
{
923
120
    decoder.user_line_begin_skip = 0;
924
120
    decoder.user_line_end_ignore = 0;
925
926
433
    for (int c = 0; c < decoder.channel_count; ++c)
927
313
    {
928
313
        exr_coding_channel_info_t& curchan = decoder.channels[c];
929
313
        uint8_t*                   ptr;
930
313
        const Slice*               fbslice;
931
932
313
        fbslice = outfb->findSlice (curchan.channel_name);
933
934
313
        if (curchan.height == 0 || !fbslice)
935
114
        {
936
114
            curchan.decode_to_ptr     = NULL;
937
114
            curchan.user_pixel_stride = 0;
938
114
            curchan.user_line_stride  = 0;
939
114
            continue;
940
114
        }
941
942
199
        if (fbslice->xSampling != 1 || fbslice->ySampling != 1)
943
0
            throw IEX_NAMESPACE::ArgExc ("Tiled data should not have subsampling.");
944
945
199
        int xOffset = fbslice->xTileCoords ? 0 : t_absX;
946
199
        int yOffset = fbslice->yTileCoords ? 0 : t_absY;
947
948
199
        curchan.user_bytes_per_element = (fbslice->type == HALF) ? 2 : 4;
949
199
        curchan.user_data_type         = (exr_pixel_type_t)fbslice->type;
950
199
        curchan.user_pixel_stride      = fbslice->xStride;
951
199
        curchan.user_line_stride       = fbslice->yStride;
952
953
199
        ptr  = reinterpret_cast<uint8_t*> (fbslice->base);
954
199
        ptr += int64_t (xOffset) * int64_t (fbslice->xStride);
955
199
        ptr += int64_t (yOffset) * int64_t (fbslice->yStride);
956
957
199
        curchan.decode_to_ptr = ptr;
958
199
    }
959
120
}
960
961
////////////////////////////////////////
962
963
void TileProcess::run_fill (
964
    const FrameBuffer *outfb, int fb_absX, int fb_absY, int t_absX, int t_absY,
965
    const std::vector<Slice> &filllist)
966
75
{
967
75
    for (auto& s: filllist)
968
40
    {
969
40
        uint8_t* ptr;
970
971
40
        if (s.xSampling != 1 || s.ySampling != 1)
972
1
            throw IEX_NAMESPACE::ArgExc ("Tiled data should not have subsampling.");
973
974
39
        int xOffset = s.xTileCoords ? 0 : t_absX;
975
39
        int yOffset = s.yTileCoords ? 0 : t_absY;
976
977
39
        ptr  = reinterpret_cast<uint8_t*> (s.base);
978
39
        ptr += int64_t (xOffset) * int64_t (s.xStride);
979
39
        ptr += int64_t (yOffset) * int64_t (s.yStride);
980
981
        // TODO: update ImfMisc, lift fill type / value
982
1.55k
        for ( int start = 0; start < cinfo.height; ++start )
983
1.51k
        {
984
1.51k
            if (start % s.ySampling) continue;
985
986
1.51k
            uint8_t* outptr = ptr;
987
43.6M
            for ( int sx = 0; sx < cinfo.width; ++sx )
988
43.6M
            {
989
43.6M
                if (sx % s.xSampling) continue;
990
991
43.6M
                switch (s.type)
992
43.6M
                {
993
0
                    case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT:
994
0
                    {
995
0
                        unsigned int fillVal = (unsigned int) (s.fillValue);
996
0
                        *(unsigned int*)outptr = fillVal;
997
0
                        break;
998
0
                    }
999
1000
43.6M
                    case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF:
1001
43.6M
                    {
1002
43.6M
                        half fillVal = half (s.fillValue);
1003
43.6M
                        *(half*)outptr = fillVal;
1004
43.6M
                        break;
1005
0
                    }
1006
1007
0
                    case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT:
1008
0
                    {
1009
0
                        float fillVal = float (s.fillValue);
1010
0
                        *(float*)outptr = fillVal;
1011
0
                        break;
1012
0
                    }
1013
0
                    default:
1014
0
                        throw IEX_NAMESPACE::ArgExc ("Unknown pixel data type.");
1015
43.6M
                }
1016
43.6M
                outptr += s.xStride;
1017
43.6M
            }
1018
1019
1.51k
            ptr += s.yStride;
1020
1.51k
        }
1021
39
    }
1022
75
}
1023
1024
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT