Coverage Report

Created: 2026-10-27 06:57

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 = EXR_DECODE_PIPELINE_INITIALIZER;
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
    const Slice& sampleCountSlice = frameBuffer.getSampleCountSlice ();
283
0
    if (!sampleCountSlice.base)
284
0
    {
285
0
        throw IEX_NAMESPACE::ArgExc (
286
0
            "Invalid base pointer, please set a proper sample count slice.");
287
0
    }
288
0
    if (sampleCountSlice.type != UINT)
289
0
    {
290
0
        throw IEX_NAMESPACE::ArgExc (
291
0
            "The type of sample count slice should be UINT.");
292
0
    }
293
294
0
    _data->frameBuffer = frameBuffer;
295
0
    _data->frameBufferValid = true;
296
0
}
297
298
const DeepFrameBuffer&
299
DeepTiledInputFile::frameBuffer () const
300
0
{
301
0
#if ILMTHREAD_THREADING_ENABLED
302
0
    std::lock_guard<std::mutex> lock (_data->_mx);
303
0
#endif
304
0
    return _data->frameBuffer;
305
0
}
306
307
bool
308
DeepTiledInputFile::isComplete () const
309
0
{
310
0
    return _ctxt.chunkTableValid (_data->partNumber);
311
0
}
312
313
void
314
DeepTiledInputFile::readTiles (
315
    int dx1, int dx2, int dy1, int dy2, int lx, int ly)
316
0
{
317
    //
318
    // Read a range of tiles from the file into the framebuffer
319
    //
320
321
0
    try
322
0
    {
323
0
        if (!_data->frameBufferValid)
324
0
        {
325
0
            throw IEX_NAMESPACE::ArgExc (
326
0
                "readTiles called with no valid frame buffer");
327
0
        }
328
329
0
        if (!isValidLevel (lx, ly))
330
0
            THROW (
331
0
                IEX_NAMESPACE::ArgExc,
332
0
                "Level coordinate "
333
0
                "(" << lx
334
0
                    << ", " << ly
335
0
                    << ") "
336
0
                       "is invalid.");
337
338
0
        if (dx1 > dx2) std::swap (dx1, dx2);
339
0
        if (dy1 > dy2) std::swap (dy1, dy2);
340
341
0
        _data->readTiles (dx1, dx2, dy1, dy2, lx, ly, false);
342
0
    }
343
0
    catch (IEX_NAMESPACE::BaseExc& e)
344
0
    {
345
0
        REPLACE_EXC (
346
0
            e,
347
0
            "Error reading deep tiled data from image "
348
0
            "file \""
349
0
                << fileName () << "\". " << e.what ());
350
0
        throw;
351
0
    }
352
0
}
353
354
void
355
DeepTiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int l)
356
0
{
357
0
    readTiles (dx1, dx2, dy1, dy2, l, l);
358
0
}
359
360
void
361
DeepTiledInputFile::readTile (int dx, int dy, int lx, int ly)
362
0
{
363
0
    readTiles (dx, dx, dy, dy, lx, ly);
364
0
}
365
366
void
367
DeepTiledInputFile::readTile (int dx, int dy, int l)
368
0
{
369
0
    readTile (dx, dy, l, l);
370
0
}
371
372
#pragma pack(push, 1)
373
struct DeepTileChunkHeader
374
{
375
    int32_t dx;
376
    int32_t dy;
377
    int32_t lx;
378
    int32_t ly;
379
    uint64_t packedCountSize;
380
    uint64_t packedDataSize;
381
    uint64_t unpackedDataSize;
382
};
383
#pragma pack(pop)
384
385
void
386
DeepTiledInputFile::rawTileData (
387
    int&      dx,
388
    int&      dy,
389
    int&      lx,
390
    int&      ly,
391
    char*     pixelData,
392
    uint64_t& pixelDataSize) const
393
0
{
394
0
    exr_chunk_info_t cinfo;
395
396
0
    static_assert (sizeof(DeepTileChunkHeader) == 40, "Expect a 40-byte tiled chunk header");
397
398
0
    if (EXR_ERR_SUCCESS == exr_read_tile_chunk_info (
399
0
            _ctxt, _data->partNumber, dx, dy, lx, ly, &cinfo))
400
0
    {
401
0
        uint64_t cbytes;
402
0
        cbytes = sizeof (DeepTileChunkHeader);
403
0
        cbytes += cinfo.sample_count_table_size;
404
0
        cbytes += cinfo.packed_size;
405
406
0
        if (!pixelData || cbytes > pixelDataSize)
407
0
        {
408
0
            pixelDataSize = cbytes;
409
0
            return;
410
0
        }
411
412
0
        pixelDataSize = cbytes;
413
414
0
        DeepTileChunkHeader* dch = reinterpret_cast<DeepTileChunkHeader*> (pixelData);
415
0
        dch->dx = cinfo.start_x;
416
0
        dch->dy = cinfo.start_y;
417
0
        dch->lx = cinfo.level_x;
418
0
        dch->ly = cinfo.level_y;
419
0
        dch->packedCountSize = cinfo.sample_count_table_size;
420
0
        dch->packedDataSize = cinfo.packed_size;
421
0
        dch->unpackedDataSize = cinfo.unpacked_size;
422
423
0
        pixelData += sizeof(DeepTileChunkHeader);
424
425
0
        if (EXR_ERR_SUCCESS !=
426
0
            exr_read_deep_chunk (
427
0
                _ctxt,
428
0
                _data->partNumber,
429
0
                &cinfo,
430
0
                pixelData + cinfo.sample_count_table_size,
431
0
                pixelData))
432
0
        {
433
0
            THROW (
434
0
                IEX_NAMESPACE::ArgExc,
435
0
                "Error reading deep tiled data from image "
436
0
                "file \""
437
0
                << fileName () << "\". Unable to read raw tile data of "
438
0
                << pixelDataSize << " bytes.");
439
0
        }
440
0
    }
441
0
    else
442
0
    {
443
0
        THROW (
444
0
            IEX_NAMESPACE::ArgExc,
445
0
            "Error reading deep tile data from image "
446
0
            "file \""
447
0
            << fileName ()
448
0
            << "\". Unable to query data block information.");
449
0
    }
450
0
}
451
452
unsigned int
453
DeepTiledInputFile::tileXSize () const
454
0
{
455
0
    return _data->tile_x_size;
456
0
}
457
458
unsigned int
459
DeepTiledInputFile::tileYSize () const
460
0
{
461
0
    return _data->tile_y_size;
462
0
}
463
464
LevelMode
465
DeepTiledInputFile::levelMode () const
466
0
{
467
0
    return (LevelMode)_data->tile_level_mode;
468
0
}
469
470
LevelRoundingMode
471
DeepTiledInputFile::levelRoundingMode () const
472
0
{
473
0
    return (LevelRoundingMode)_data->tile_round_mode;
474
0
}
475
476
int
477
DeepTiledInputFile::numLevels () const
478
0
{
479
0
    if (levelMode () == RIPMAP_LEVELS)
480
0
        THROW (
481
0
            IEX_NAMESPACE::LogicExc,
482
0
            "Error calling numLevels() on image "
483
0
            "file \""
484
0
                << fileName ()
485
0
                << "\" "
486
0
                   "(numLevels() is not defined for files "
487
0
                   "with RIPMAP level mode).");
488
489
0
    return _data->num_x_levels;
490
0
}
491
492
int
493
DeepTiledInputFile::numXLevels () const
494
0
{
495
0
    return _data->num_x_levels;
496
0
}
497
498
int
499
DeepTiledInputFile::numYLevels () const
500
0
{
501
0
    return _data->num_y_levels;
502
0
}
503
504
bool
505
DeepTiledInputFile::isValidLevel (int lx, int ly) const
506
0
{
507
0
    if (lx < 0 || ly < 0) return false;
508
509
0
    if (levelMode () == MIPMAP_LEVELS && lx != ly) return false;
510
511
0
    if (lx >= numXLevels () || ly >= numYLevels ()) return false;
512
513
0
    return true;
514
0
}
515
516
int
517
DeepTiledInputFile::levelWidth (int lx) const
518
0
{
519
0
    int32_t levw = 0;
520
0
    if (EXR_ERR_SUCCESS != exr_get_level_sizes (
521
0
            _ctxt, _data->partNumber, lx, 0, &levw, nullptr))
522
0
    {
523
0
        THROW (
524
0
            IEX_NAMESPACE::ArgExc,
525
0
            "Error calling levelWidth() on image "
526
0
            "file \""
527
0
                << fileName () << "\".");
528
0
    }
529
0
    return levw;
530
0
}
531
532
int
533
DeepTiledInputFile::levelHeight (int ly) const
534
0
{
535
0
    int32_t levh = 0;
536
0
    if (EXR_ERR_SUCCESS != exr_get_level_sizes (
537
0
            _ctxt, _data->partNumber, 0, ly, nullptr, &levh))
538
0
    {
539
0
        THROW (
540
0
            IEX_NAMESPACE::ArgExc,
541
0
            "Error calling levelWidth() on image "
542
0
            "file \""
543
0
                << fileName () << "\".");
544
0
    }
545
0
    return levh;
546
0
}
547
548
int
549
DeepTiledInputFile::numXTiles (int lx) const
550
0
{
551
0
    int32_t countx = 0;
552
0
    if (EXR_ERR_SUCCESS != exr_get_tile_counts (
553
0
            _ctxt, _data->partNumber, lx, 0, &countx, nullptr))
554
0
    {
555
0
        THROW (
556
0
            IEX_NAMESPACE::ArgExc,
557
0
            "Error calling numXTiles() on image "
558
0
            "file \""
559
0
                << fileName () << "\".");
560
0
    }
561
0
    return countx;
562
0
}
563
564
int
565
DeepTiledInputFile::numYTiles (int ly) const
566
0
{
567
0
    int32_t county = 0;
568
0
    if (EXR_ERR_SUCCESS != exr_get_tile_counts (
569
0
            _ctxt, _data->partNumber, 0, ly, nullptr, &county))
570
0
    {
571
0
        THROW (
572
0
            IEX_NAMESPACE::ArgExc,
573
0
            "Error calling numYTiles() on image "
574
0
            "file \""
575
0
                << fileName () << "\".");
576
0
    }
577
0
    return county;
578
0
}
579
580
IMATH_NAMESPACE::Box2i
581
DeepTiledInputFile::dataWindowForLevel (int l) const
582
0
{
583
0
    return dataWindowForLevel (l, l);
584
0
}
585
586
IMATH_NAMESPACE::Box2i
587
DeepTiledInputFile::dataWindowForLevel (int lx, int ly) const
588
0
{
589
0
    int32_t levw = 0, levh = 0;
590
0
    if (EXR_ERR_SUCCESS != exr_get_level_sizes (
591
0
            _ctxt, _data->partNumber, lx, ly, &levw, &levh))
592
0
    {
593
0
        THROW (
594
0
            IEX_NAMESPACE::ArgExc,
595
0
            "Error calling dataWindowForLevel() on image "
596
0
            "file \""
597
0
                << fileName () << "\".");
598
0
    }
599
0
    exr_attr_box2i_t dw = _ctxt.dataWindow (_data->partNumber);
600
0
    return IMATH_NAMESPACE::Box2i (
601
0
        IMATH_NAMESPACE::V2i (dw.min.x, dw.min.y),
602
0
        IMATH_NAMESPACE::V2i (dw.min.x + levw - 1, dw.min.y + levh - 1));
603
0
}
604
605
IMATH_NAMESPACE::Box2i
606
DeepTiledInputFile::dataWindowForTile (int dx, int dy, int l) const
607
0
{
608
0
    return dataWindowForTile (dx, dy, l, l);
609
0
}
610
611
IMATH_NAMESPACE::Box2i
612
DeepTiledInputFile::dataWindowForTile (int dx, int dy, int lx, int ly) const
613
0
{
614
0
    try
615
0
    {
616
0
        if (!isValidTile (dx, dy, lx, ly))
617
0
            throw IEX_NAMESPACE::ArgExc ("Arguments not in valid range.");
618
619
        //exr_attr_box2i_t dw = _ctxt.dataWindow (_data->partNumber);
620
0
        auto dw = dataWindowForLevel (lx, ly);
621
622
0
        int32_t tileSizeX, tileSizeY;
623
0
        if (EXR_ERR_SUCCESS !=
624
0
            exr_get_tile_sizes (_ctxt, _data->partNumber, lx, ly, &tileSizeX, &tileSizeY))
625
0
          throw IEX_NAMESPACE::ArgExc ("Unable to query the data window.");
626
627
0
        dw.min.x += dx * tileSizeX;
628
0
        dw.min.y += dy * tileSizeY;
629
0
        int limX = dw.min.x + tileSizeX - 1;
630
0
        int limY = dw.min.y + tileSizeY - 1;
631
0
        limX = std::min (limX, dw.max.x);
632
0
        limY = std::min (limY, dw.max.y);
633
634
0
        return IMATH_NAMESPACE::Box2i (
635
0
            IMATH_NAMESPACE::V2i (dw.min.x, dw.min.y),
636
0
            IMATH_NAMESPACE::V2i (limX, limY));
637
0
    }
638
0
    catch (IEX_NAMESPACE::BaseExc& e)
639
0
    {
640
0
        REPLACE_EXC (
641
0
            e,
642
0
            "Error calling dataWindowForTile() on image "
643
0
            "file \""
644
0
                << fileName () << "\". " << e.what ());
645
0
        throw;
646
0
    }
647
0
}
648
649
bool
650
DeepTiledInputFile::isValidTile (int dx, int dy, int lx, int ly) const
651
0
{
652
0
    int32_t countx = 0, county = 0;
653
0
    if (EXR_ERR_SUCCESS == exr_get_tile_counts (
654
0
            _ctxt, _data->partNumber, lx, ly, &countx, &county))
655
0
    {
656
        // get tile counts will check lx, ly for us
657
0
        return ((dx < countx && dx >= 0) &&
658
0
                (dy < county && dy >= 0));
659
0
    }
660
0
    return false;
661
0
}
662
663
void
664
DeepTiledInputFile::readPixelSampleCounts (
665
    int dx1, int dx2, int dy1, int dy2, int lx, int ly)
666
0
{
667
    //
668
    // Read a range of tiles from the file into the framebuffer
669
    //
670
0
    try
671
0
    {
672
0
        if (!_data->frameBufferValid)
673
0
        {
674
0
            throw IEX_NAMESPACE::ArgExc (
675
0
                "readPixelSampleCounts called with no valid frame buffer");
676
0
        }
677
678
0
        if (!isValidLevel (lx, ly))
679
0
            THROW (
680
0
                IEX_NAMESPACE::ArgExc,
681
0
                "Level coordinate "
682
0
                "(" << lx
683
0
                    << ", " << ly
684
0
                    << ") "
685
0
                       "is invalid.");
686
687
0
        if (dx1 > dx2) std::swap (dx1, dx2);
688
0
        if (dy1 > dy2) std::swap (dy1, dy2);
689
690
0
        _data->readTiles (dx1, dx2, dy1, dy2, lx, ly, true);
691
0
    }
692
0
    catch (IEX_NAMESPACE::BaseExc& e)
693
0
    {
694
0
        REPLACE_EXC (
695
0
            e,
696
0
            "Error reading deep pixel sample counts data from image "
697
0
            "file \""
698
0
                << fileName () << "\". " << e.what ());
699
0
        throw;
700
0
    }
701
0
}
702
703
void
704
DeepTiledInputFile::readPixelSampleCount (int dx, int dy, int l)
705
0
{
706
0
    readPixelSampleCount (dx, dy, l, l);
707
0
}
708
709
void
710
DeepTiledInputFile::readPixelSampleCount (int dx, int dy, int lx, int ly)
711
0
{
712
0
    readPixelSampleCounts (dx, dx, dy, dy, lx, ly);
713
0
}
714
715
void
716
DeepTiledInputFile::readPixelSampleCounts (
717
    int dx1, int dx2, int dy1, int dy2, int l)
718
0
{
719
0
    readPixelSampleCounts (dx1, dx2, dy1, dy2, l, l);
720
0
}
721
722
size_t
723
DeepTiledInputFile::totalTiles () const
724
0
{
725
    //
726
    // Calculate the total number of tiles in the file
727
    //
728
729
0
    int numAllTiles = 0;
730
731
0
    switch (levelMode ())
732
0
    {
733
0
        case ONE_LEVEL:
734
0
        case MIPMAP_LEVELS:
735
736
0
            for (int i_l = 0; i_l < numLevels (); ++i_l)
737
0
                numAllTiles += numXTiles (i_l) * numYTiles (i_l);
738
739
0
            break;
740
741
0
        case RIPMAP_LEVELS:
742
743
0
            for (int i_ly = 0; i_ly < numYLevels (); ++i_ly)
744
0
                for (int i_lx = 0; i_lx < numXLevels (); ++i_lx)
745
0
                    numAllTiles += numXTiles (i_lx) * numYTiles (i_ly);
746
747
0
            break;
748
749
0
        default: throw IEX_NAMESPACE::ArgExc ("Unknown LevelMode format.");
750
0
    }
751
0
    return numAllTiles;
752
0
}
753
754
namespace
755
{
756
struct tilepos
757
{
758
    uint64_t filePos;
759
    int      dx;
760
    int      dy;
761
    int      lx;
762
    int      ly;
763
    bool     operator< (const tilepos& other) const
764
0
    {
765
0
        return filePos < other.filePos;
766
0
    }
767
};
768
} // namespace
769
770
void
771
DeepTiledInputFile::getTileOrder (int dx[], int dy[], int lx[], int ly[]) const
772
0
{
773
    // TODO: remove once TiledOutputFile copy is converted
774
0
    switch (_ctxt.lineOrder (_data->partNumber))
775
0
    {
776
0
        case EXR_LINEORDER_RANDOM_Y:
777
            // calc below outside the nest
778
0
            break;
779
780
0
        case EXR_LINEORDER_DECREASING_Y:
781
0
        {
782
0
            dx[0] = 0;
783
0
            dy[0] = numYTiles (0) - 1;
784
0
            lx[0] = 0;
785
0
            ly[0] = 0;
786
0
            return;
787
0
        }
788
0
        case EXR_LINEORDER_INCREASING_Y:
789
0
            dx[0] = 0;
790
0
            dy[0] = 0;
791
0
            lx[0] = 0;
792
0
            ly[0] = 0;
793
0
            return;
794
795
0
        case EXR_LINEORDER_LAST_TYPE: /* invalid but should never be here */
796
0
        default:
797
0
            throw IEX_NAMESPACE::ArgExc ("Unknown LineOrder.");
798
0
    }
799
800
0
    size_t numAllTiles = 0;
801
0
    int numX = numXLevels ();
802
0
    int numY = numYLevels ();
803
804
0
    switch (levelMode ())
805
0
    {
806
0
        case ONE_LEVEL:
807
0
        case MIPMAP_LEVELS:
808
0
            for (int i_l = 0; i_l < numY; ++i_l)
809
0
                numAllTiles += size_t (numXTiles (i_l)) * size_t (numYTiles (i_l));
810
0
            break;
811
812
0
        case RIPMAP_LEVELS:
813
0
            for (int i_ly = 0; i_ly < numY; ++i_ly)
814
0
                for (int i_lx = 0; i_lx < numX; ++i_lx)
815
0
                    numAllTiles += size_t (numXTiles (i_lx)) * size_t (numYTiles (i_ly));
816
0
            break;
817
818
0
        default:
819
0
            throw IEX_NAMESPACE::ArgExc ("Unknown LevelMode format.");
820
0
    }
821
822
0
    std::vector<tilepos> table;
823
824
0
    table.resize (numAllTiles);
825
0
    size_t tIdx = 0;
826
0
    switch (levelMode ())
827
0
    {
828
0
        case ONE_LEVEL:
829
0
        case MIPMAP_LEVELS:
830
0
            for (int i_l = 0; i_l < numY; ++i_l)
831
0
            {
832
0
                int nY = numYTiles (i_l);
833
0
                int nX = numXTiles (i_l);
834
835
0
                for ( int y = 0; y < nY; ++y )
836
0
                    for ( int x = 0; x < nX; ++x )
837
0
                    {
838
0
                        exr_chunk_info_t cinfo;
839
0
                        if (EXR_ERR_SUCCESS == exr_read_tile_chunk_info (
840
0
                                _ctxt, _data->partNumber, x, y, i_l, i_l, &cinfo))
841
0
                        {
842
0
                            tilepos &tp = table[tIdx++];
843
0
                            tp.filePos = cinfo.data_offset;
844
0
                            tp.dx = x;
845
0
                            tp.dy = y;
846
0
                            tp.lx = i_l;
847
0
                            tp.ly = i_l;
848
0
                        }
849
0
                        else
850
0
                        {
851
0
                            throw IEX_NAMESPACE::ArgExc ("Unable to get tile offset.");
852
0
                        }
853
0
                    }
854
0
            }
855
0
            break;
856
857
0
        case RIPMAP_LEVELS:
858
0
            for (int i_ly = 0; i_ly < numY; ++i_ly)
859
0
            {
860
0
                int nY = numYTiles (i_ly);
861
0
                for (int i_lx = 0; i_lx < numX; ++i_lx)
862
0
                {
863
0
                    int nX = numXTiles (i_lx);
864
0
                    for ( int y = 0; y < nY; ++y )
865
0
                        for ( int x = 0; x < nX; ++x )
866
0
                        {
867
0
                            exr_chunk_info_t cinfo;
868
0
                            if (EXR_ERR_SUCCESS == exr_read_tile_chunk_info (
869
0
                                    _ctxt, _data->partNumber, x, y, i_lx, i_ly, &cinfo))
870
0
                            {
871
0
                                tilepos &tp = table[tIdx++];
872
0
                                tp.filePos = cinfo.data_offset;
873
0
                                tp.dx = x;
874
0
                                tp.dy = y;
875
0
                                tp.lx = i_lx;
876
0
                                tp.ly = i_ly;
877
0
                            }
878
0
                            else
879
0
                            {
880
0
                                throw IEX_NAMESPACE::ArgExc ("Unable to get tile offset.");
881
0
                            }
882
0
                        }
883
0
                }
884
0
            }
885
0
            break;
886
887
0
        default: throw IEX_NAMESPACE::ArgExc ("Unknown LevelMode format.");
888
0
    }
889
890
0
    std::sort (table.begin(), table.end ());
891
892
0
    for (size_t i = 0; i < numAllTiles; ++i)
893
0
    {
894
0
        const auto& tp = table[i];
895
0
        dx[i] = tp.dx;
896
0
        dy[i] = tp.dy;
897
0
        lx[i] = tp.lx;
898
0
        ly[i] = tp.ly;
899
0
    }
900
0
}
901
902
void DeepTiledInputFile::Data::readTiles (
903
    int dx1, int dx2, int dy1, int dy2, int lx, int ly, bool countsOnly)
904
0
{
905
0
    int nTiles = dx2 - dx1 + 1;
906
0
    nTiles *= dy2 - dy1 + 1;
907
908
0
    exr_chunk_info_t      cinfo;
909
0
#if ILMTHREAD_THREADING_ENABLED
910
0
    if (nTiles > 1 && numThreads > 1)
911
0
    {
912
        // we need the lifetime of this to last longer than the
913
        // lifetime of the task group below such that we don't get use
914
        // after free type error, so use scope rules to accomplish
915
        // this
916
0
        TileProcessGroup tpg (numThreads);
917
918
0
        {
919
0
            ILMTHREAD_NAMESPACE::TaskGroup tg;
920
921
0
            for (int ty = dy1; ty <= dy2; ++ty)
922
0
            {
923
0
                for (int tx = dx1; tx <= dx2; ++tx)
924
0
                {
925
0
                    exr_result_t rv = exr_read_tile_chunk_info (
926
0
                        *_ctxt, partNumber, tx, ty, lx, ly, &cinfo);
927
0
                    if (EXR_ERR_INCOMPLETE_CHUNK_TABLE == rv)
928
0
                    {
929
0
                        THROW (
930
0
                            IEX_NAMESPACE::InputExc,
931
0
                            "Tile (" << tx << ", " << ty << ", " << lx << ", " << ly
932
0
                            << ") is missing.");
933
0
                    }
934
0
                    else if (EXR_ERR_SUCCESS != rv)
935
0
                        throw IEX_NAMESPACE::InputExc ("Unable to query tile information");
936
937
0
                    ILMTHREAD_NAMESPACE::ThreadPool::addGlobalTask (
938
0
                        new TileBufferTask (&tg, this, &tpg, &frameBuffer, cinfo, countsOnly) );
939
0
                }
940
0
            }
941
0
        }
942
943
0
        tpg.throw_on_failure ();
944
0
    }
945
0
    else
946
0
#endif
947
0
    {
948
0
        TileProcess tp;
949
950
0
        tp.counts_only = countsOnly;
951
0
        for (int ty = dy1; ty <= dy2; ++ty)
952
0
        {
953
0
            for (int tx = dx1; tx <= dx2; ++tx)
954
0
            {
955
0
                exr_result_t rv = exr_read_tile_chunk_info (
956
0
                    *_ctxt, partNumber, tx, ty, lx, ly, &cinfo);
957
0
                if (EXR_ERR_INCOMPLETE_CHUNK_TABLE == rv)
958
0
                {
959
0
                    THROW (
960
0
                        IEX_NAMESPACE::InputExc,
961
0
                        "Tile (" << tx << ", " << ty << ", " << lx << ", " << ly
962
0
                        << ") is missing.");
963
0
                }
964
0
                else if (EXR_ERR_SUCCESS != rv)
965
0
                    throw IEX_NAMESPACE::InputExc ("Unable to query tile information");
966
967
0
                tp.cinfo = cinfo;
968
0
                tp.run_decode (
969
0
                    *_ctxt,
970
0
                    partNumber,
971
0
                    &frameBuffer,
972
0
                    fill_list);
973
0
            }
974
0
        }
975
0
    }
976
0
}
977
978
////////////////////////////////////////
979
980
#if ILMTHREAD_THREADING_ENABLED
981
void DeepTiledInputFile::Data::TileBufferTask::execute ()
982
0
{
983
0
    try
984
0
    {
985
0
        _tile->run_decode (
986
0
            *(_ifd->_ctxt),
987
0
            _ifd->partNumber,
988
0
            _outfb,
989
0
            _ifd->fill_list);
990
0
    }
991
0
    catch (std::exception &e)
992
0
    {
993
0
        _tile_group->record_failure (e.what ());
994
0
    }
995
0
    catch (...)
996
0
    {
997
0
        _tile_group->record_failure ("Unknown exception");
998
0
    }
999
0
}
1000
#endif
1001
1002
////////////////////////////////////////
1003
1004
void TileProcess::run_decode (
1005
    exr_const_context_t ctxt,
1006
    int pn,
1007
    const DeepFrameBuffer *outfb,
1008
    const std::vector<DeepSlice> &filllist)
1009
0
{
1010
0
    int absX, absY, tileX, tileY;
1011
0
    exr_attr_box2i_t dw;
1012
0
    uint8_t flags;
1013
1014
0
    if (first)
1015
0
    {
1016
0
        if (EXR_ERR_SUCCESS !=
1017
0
            exr_decoding_initialize (ctxt, pn, &cinfo, &decoder))
1018
0
        {
1019
0
            throw IEX_NAMESPACE::IoExc ("Unable to initialize decode pipeline");
1020
0
        }
1021
0
        decoder.decode_flags |= EXR_DECODE_NON_IMAGE_DATA_AS_POINTERS;
1022
0
        decoder.decode_flags |= EXR_DECODE_SAMPLE_COUNTS_AS_INDIVIDUAL;
1023
0
        flags = 0;
1024
1025
0
        first = false;
1026
0
    }
1027
0
    else
1028
0
    {
1029
0
        if (EXR_ERR_SUCCESS !=
1030
0
            exr_decoding_update (ctxt, pn, &cinfo, &decoder))
1031
0
        {
1032
0
            throw IEX_NAMESPACE::IoExc ("Unable to update decode pipeline");
1033
0
        }
1034
0
        flags = decoder.decode_flags;
1035
0
    }
1036
1037
0
    if (EXR_ERR_SUCCESS != exr_get_data_window (ctxt, pn, &dw))
1038
0
        throw IEX_NAMESPACE::ArgExc ("Unable to query the data window.");
1039
1040
0
    if (EXR_ERR_SUCCESS != exr_get_tile_sizes (
1041
0
            ctxt, pn, cinfo.level_x, cinfo.level_y, &tileX, &tileY))
1042
0
        throw IEX_NAMESPACE::ArgExc ("Unable to query the data window.");
1043
1044
0
    absX = dw.min.x + tileX * cinfo.start_x;
1045
0
    absY = dw.min.y + tileY * cinfo.start_y;
1046
1047
0
    if (counts_only)
1048
0
        decoder.decode_flags |= EXR_DECODE_SAMPLE_DATA_ONLY;
1049
0
    else
1050
0
        decoder.decode_flags = decoder.decode_flags & ~EXR_DECODE_SAMPLE_DATA_ONLY;
1051
1052
0
    update_pointers (outfb, dw.min.x, dw.min.y, absX, absY);
1053
1054
0
    if (flags != decoder.decode_flags)
1055
0
    {
1056
0
        if (EXR_ERR_SUCCESS !=
1057
0
            exr_decoding_choose_default_routines (ctxt, pn, &decoder))
1058
0
        {
1059
0
            throw IEX_NAMESPACE::IoExc ("Unable to choose decoder routines");
1060
0
        }
1061
0
    }
1062
1063
0
    last_decode_err = exr_decoding_run (ctxt, pn, &decoder);
1064
0
    if (EXR_ERR_SUCCESS != last_decode_err)
1065
0
    {
1066
0
        THROW (
1067
0
            IEX_NAMESPACE::IoExc,
1068
0
            "Unable to run decoder: "
1069
0
            << exr_get_error_code_as_string (last_decode_err));
1070
0
    }
1071
1072
0
    copy_sample_count (outfb, dw.min.x, dw.min.y, absX, absY);
1073
1074
0
    if (counts_only)
1075
0
        return;
1076
1077
0
    run_fill (outfb, dw.min.x, dw.min.y, absX, absY, filllist);
1078
0
}
1079
1080
////////////////////////////////////////
1081
1082
void TileProcess::update_pointers (const DeepFrameBuffer *outfb, int fb_absX, int fb_absY, int t_absX, int t_absY)
1083
0
{
1084
0
    decoder.user_line_begin_skip = 0;
1085
0
    decoder.user_line_end_ignore = 0;
1086
1087
0
    for (int c = 0; c < decoder.channel_count; ++c)
1088
0
    {
1089
0
        exr_coding_channel_info_t& curchan = decoder.channels[c];
1090
0
        uint8_t*                   ptr;
1091
0
        const DeepSlice*           fbslice;
1092
1093
0
        fbslice = outfb->findSlice (curchan.channel_name);
1094
1095
0
        if (curchan.height == 0 || !fbslice)
1096
0
        {
1097
0
            curchan.decode_to_ptr     = NULL;
1098
0
            curchan.user_pixel_stride = 0;
1099
0
            curchan.user_line_stride  = 0;
1100
0
            continue;
1101
0
        }
1102
1103
0
        if (fbslice->xSampling != 1 || fbslice->ySampling != 1)
1104
0
            throw IEX_NAMESPACE::ArgExc ("Tiled data should not have subsampling.");
1105
1106
0
        int xOffset = fbslice->xTileCoords ? 0 : t_absX;
1107
0
        int yOffset = fbslice->yTileCoords ? 0 : t_absY;
1108
1109
0
        curchan.user_bytes_per_element = fbslice->sampleStride;
1110
0
        curchan.user_data_type         = (exr_pixel_type_t)fbslice->type;
1111
0
        curchan.user_pixel_stride      = fbslice->xStride;
1112
0
        curchan.user_line_stride       = fbslice->yStride;
1113
1114
0
        ptr  = reinterpret_cast<uint8_t*> (fbslice->base);
1115
0
        ptr += int64_t (xOffset) * int64_t (fbslice->xStride);
1116
0
        ptr += int64_t (yOffset) * int64_t (fbslice->yStride);
1117
1118
0
        curchan.decode_to_ptr = ptr;
1119
0
    }
1120
0
}
1121
1122
////////////////////////////////////////
1123
1124
void TileProcess::run_fill (
1125
    const DeepFrameBuffer *outfb, int fb_absX, int fb_absY, int t_absX, int t_absY,
1126
    const std::vector<DeepSlice> &filllist)
1127
0
{
1128
0
    for (auto& fills: filllist)
1129
0
    {
1130
0
        uint8_t* ptr;
1131
1132
0
        if (fills.xSampling != 1 || fills.ySampling != 1)
1133
0
            throw IEX_NAMESPACE::ArgExc ("Tiled data should not have subsampling.");
1134
1135
0
        int xOffset = fills.xTileCoords ? 0 : t_absX;
1136
0
        int yOffset = fills.yTileCoords ? 0 : t_absY;
1137
1138
0
        ptr  = reinterpret_cast<uint8_t*> (fills.base);
1139
0
        ptr += int64_t (xOffset) * int64_t (fills.xStride);
1140
0
        ptr += int64_t (yOffset) * int64_t (fills.yStride);
1141
1142
        // TODO: update ImfMisc, lift fill type / value
1143
0
        for ( int start = 0; start < cinfo.height; ++start )
1144
0
        {
1145
0
            const int32_t* counts = decoder.sample_count_table;
1146
0
            counts += (int64_t) start * (int64_t) cinfo.width;
1147
1148
0
            uint8_t* outptr = ptr;
1149
0
            for ( int sx = 0; sx < cinfo.width; ++sx )
1150
0
            {
1151
0
                int32_t samps = counts[sx];
1152
0
                void *dest = *((void **)outptr);
1153
1154
0
                if (samps == 0 || dest == nullptr)
1155
0
                {
1156
0
                    outptr += fills.xStride;
1157
0
                    continue;
1158
0
                }
1159
1160
0
                switch (fills.type)
1161
0
                {
1162
0
                    case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT:
1163
0
                    {
1164
0
                        unsigned int fillVal = (unsigned int) (fills.fillValue);
1165
0
                        unsigned int* fillptr = static_cast<unsigned int*> (dest);
1166
1167
0
                        for ( int32_t s = 0; s < samps; ++s )
1168
0
                            fillptr[s] = fillVal;
1169
0
                        break;
1170
0
                    }
1171
1172
0
                    case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF:
1173
0
                    {
1174
0
                        half fillVal = half (fills.fillValue);
1175
0
                        half* fillptr = static_cast<half*> (dest);
1176
1177
0
                        for ( int32_t s = 0; s < samps; ++s )
1178
0
                            fillptr[s] = fillVal;
1179
0
                        break;
1180
0
                    }
1181
1182
0
                    case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT:
1183
0
                    {
1184
0
                        float fillVal = float (fills.fillValue);
1185
0
                        float* fillptr = static_cast<float*> (dest);
1186
1187
0
                        for ( int32_t s = 0; s < samps; ++s )
1188
0
                            fillptr[s] = fillVal;
1189
0
                        break;
1190
0
                    }
1191
0
                    default:
1192
0
                        throw IEX_NAMESPACE::ArgExc ("Unknown pixel data type.");
1193
0
                }
1194
0
                outptr += fills.xStride;
1195
0
            }
1196
1197
0
            ptr += fills.yStride;
1198
0
        }
1199
0
    }
1200
0
}
1201
1202
////////////////////////////////////////
1203
1204
void TileProcess::copy_sample_count (
1205
    const DeepFrameBuffer *outfb,
1206
    int fb_absX, int fb_absY,
1207
    int t_absX, int t_absY)
1208
0
{
1209
0
    uint8_t*     ptr;
1210
0
    const Slice& s = outfb->getSampleCountSlice ();
1211
0
    if (s.xSampling != 1 || s.ySampling != 1)
1212
0
        throw IEX_NAMESPACE::ArgExc ("Tiled data should not have subsampling.");
1213
1214
0
    if (s.base == nullptr)
1215
0
        throw IEX_NAMESPACE::ArgExc (
1216
0
            "Deep frame buffer is missing sample counts; call insertSampleCountSlice before reading.");
1217
1218
0
    if (s.type != UINT)
1219
0
        throw IEX_NAMESPACE::ArgExc (
1220
0
            "The type of sample count slice should be UINT.");
1221
1222
0
    int xOffset = s.xTileCoords ? 0 : t_absX;
1223
0
    int yOffset = s.yTileCoords ? 0 : t_absY;
1224
1225
0
    ptr  = reinterpret_cast<uint8_t*> (s.base);
1226
0
    ptr += int64_t (xOffset) * int64_t (s.xStride);
1227
0
    ptr += int64_t (yOffset) * int64_t (s.yStride);
1228
1229
0
    int64_t xS = int64_t (s.xStride);
1230
0
    int64_t yS = int64_t (s.yStride);
1231
1232
0
    for ( int y = 0; y < cinfo.height; ++y )
1233
0
    {
1234
0
        const int32_t* counts = decoder.sample_count_table + y * cinfo.width;
1235
1236
0
        if (xS == sizeof(int32_t))
1237
0
        {
1238
0
            memcpy (ptr, counts, cinfo.width * sizeof(int32_t));
1239
0
        }
1240
0
        else
1241
0
        {
1242
0
            uint8_t* lineptr = ptr;
1243
0
            for ( int x = 0; x < cinfo.width; ++x )
1244
0
            {
1245
0
                *((int32_t *)lineptr) = counts[x];
1246
0
                lineptr += xS;
1247
0
            }
1248
0
        }
1249
0
        ptr += yS;
1250
0
    }
1251
0
}
1252
1253
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT