Coverage Report

Created: 2026-01-25 07:18

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