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/ImfDeepScanLineInputFile.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 DeepScanLineInputFile
9
//
10
//-----------------------------------------------------------------------------
11
12
#include "ImfDeepScanLineInputFile.h"
13
14
#include "ImfDeepFrameBuffer.h"
15
#include "ImfInputPartData.h"
16
17
#include "IlmThreadPool.h"
18
#if ILMTHREAD_THREADING_ENABLED
19
#    include "IlmThreadProcessGroup.h"
20
#    include <mutex>
21
#endif
22
23
#include "Iex.h"
24
25
#include <algorithm>
26
#include <limits>
27
#include <string>
28
#include <vector>
29
30
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
31
32
namespace {
33
34
// same as normal scanline but w/ deep framebuffer
35
// TODO: template?
36
struct ScanLineProcess
37
{
38
    ~ScanLineProcess ()
39
10.7k
    {
40
10.7k
        if (!first)
41
1.26k
            exr_decoding_destroy (decoder.context, &decoder);
42
10.7k
    }
43
44
    void run_mem_decode (
45
        exr_const_context_t ctxt,
46
        int pn,
47
        const char *rawdata,
48
        const DeepFrameBuffer *outfb,
49
        int fbY,
50
        int fbLastY,
51
        const std::vector<DeepSlice> &filllist);
52
53
    void run_decode (
54
        exr_const_context_t ctxt,
55
        int pn,
56
        const DeepFrameBuffer *outfb,
57
        int fbY,
58
        int fbLastY,
59
        const std::vector<DeepSlice> &filllist);
60
61
    void run_unpack (
62
        exr_const_context_t ctxt,
63
        int pn,
64
        const DeepFrameBuffer *outfb,
65
        int fbY,
66
        int fbLastY,
67
        const std::vector<DeepSlice> &filllist);
68
69
    void update_pointers (
70
        const DeepFrameBuffer *outfb,
71
        int fbY,
72
        int fbLastY);
73
74
    void run_fill (
75
        const DeepFrameBuffer *outfb,
76
        int fbY,
77
        const std::vector<DeepSlice> &filllist);
78
79
    void copy_sample_count (
80
        const DeepFrameBuffer *outfb,
81
        int fbY);
82
83
    exr_result_t          last_decode_err = EXR_ERR_UNKNOWN;
84
    bool                  first = true;
85
    bool                  counts_only = false;
86
    exr_chunk_info_t      cinfo;
87
    exr_decode_pipeline_t decoder = EXR_DECODE_PIPELINE_INITIALIZER;
88
89
    ScanLineProcess*      next;
90
};
91
92
#if ILMTHREAD_THREADING_ENABLED
93
using ScanLineProcessGroup = ILMTHREAD_NAMESPACE::ProcessGroup<ScanLineProcess>;
94
#endif
95
96
} // empty namespace
97
98
struct DeepScanLineInputFile::Data
99
{
100
    Data (Context *ctxt, int pN, int nT)
101
1.08k
    : _ctxt (ctxt)
102
1.08k
    , partNumber (pN)
103
1.08k
    , numThreads (nT)
104
1.08k
    {}
105
106
    void initialize ()
107
1.08k
    {
108
1.08k
        if (_ctxt->storage (partNumber) != EXR_STORAGE_DEEP_SCANLINE)
109
0
            throw IEX_NAMESPACE::ArgExc ("File part is not a deep scanline part");
110
111
1.08k
        version = _ctxt->version ();
112
1.08k
    }
113
114
    std::pair<int, int> getChunkRange (int y) const;
115
116
    void readData (const DeepFrameBuffer &fb, int scanLine1, int scanLine2, bool countsOnly);
117
    void readMemData (
118
        const DeepFrameBuffer &fb,
119
        const char *rawPixelData,
120
        int scanLine1,
121
        int scanLine2,
122
        bool countsOnly);
123
124
    void prepFillList (const DeepFrameBuffer &fb, std::vector<DeepSlice> &fill);
125
126
    Context* _ctxt;
127
    int partNumber;
128
    int numThreads;
129
    int version;
130
    Header header;
131
    bool header_filled = false;
132
133
    bool frameBufferValid = false;
134
    DeepFrameBuffer frameBuffer;
135
    std::vector<DeepSlice> fill_list;
136
137
#if ILMTHREAD_THREADING_ENABLED
138
    std::mutex _mx;
139
140
    class LineBufferTask final : public ILMTHREAD_NAMESPACE::Task
141
    {
142
    public:
143
        LineBufferTask (
144
            ILMTHREAD_NAMESPACE::TaskGroup* group,
145
            Data*                   ifd,
146
            ScanLineProcessGroup*   lineg,
147
            const DeepFrameBuffer*  outfb,
148
            const exr_chunk_info_t& cinfo,
149
            int                     fby,
150
            int                     endScan,
151
            bool                    countsOnly)
152
48
            : Task (group)
153
48
            , _outfb (outfb)
154
48
            , _ifd (ifd)
155
48
            , _fby (fby)
156
48
            , _last_fby (endScan)
157
48
            , _line (lineg->pop ())
158
48
            , _line_group (lineg)
159
48
        {
160
48
            _line->cinfo = cinfo;
161
48
            _line->counts_only = countsOnly;
162
48
        }
163
164
        ~LineBufferTask () override
165
48
        {
166
48
            _line_group->push (_line);
167
48
        }
168
169
        void execute () override;
170
171
    private:
172
        void run_decode ();
173
174
        const DeepFrameBuffer* _outfb;
175
        Data*                  _ifd;
176
        int                    _fby;
177
        int                    _last_fby;
178
        ScanLineProcess*       _line;
179
        ScanLineProcessGroup*  _line_group;
180
    };
181
#endif
182
};
183
184
DeepScanLineInputFile::DeepScanLineInputFile (InputPartData* part)
185
1.08k
    : _ctxt (part->context),
186
1.08k
      _data (std::make_shared<Data> (&_ctxt, part->partNumber, part->numThreads))
187
1.08k
{
188
1.08k
    _data->initialize ();
189
1.08k
}
190
191
DeepScanLineInputFile::DeepScanLineInputFile (
192
    const char*               filename,
193
    const ContextInitializer& ctxtinit,
194
    int                       numThreads)
195
0
    : _ctxt (filename, ctxtinit, Context::read_mode_t{})
196
0
    , _data (std::make_shared<Data> (&_ctxt, 0, numThreads))
197
0
{
198
0
    _data->initialize ();
199
0
}
200
201
DeepScanLineInputFile::DeepScanLineInputFile (
202
    const char fileName[], int numThreads)
203
0
    : DeepScanLineInputFile (
204
0
        fileName,
205
0
        ContextInitializer ()
206
0
        .silentHeaderParse (true)
207
0
        .strictHeaderValidation (false),
208
0
        numThreads)
209
0
{
210
0
}
211
212
DeepScanLineInputFile::DeepScanLineInputFile (
213
    OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is, int numThreads)
214
0
    : DeepScanLineInputFile (
215
0
        is.fileName (),
216
0
        ContextInitializer ()
217
0
        .silentHeaderParse (true)
218
0
        .strictHeaderValidation (false)
219
0
        .setInputStream (&is),
220
0
        numThreads)
221
0
{
222
0
}
223
224
DeepScanLineInputFile::DeepScanLineInputFile (
225
    const Header&                            header,
226
    OPENEXR_IMF_INTERNAL_NAMESPACE::IStream* is,
227
    int                                      version,
228
    int                                      numThreads)
229
0
    : DeepScanLineInputFile (
230
0
        is->fileName (),
231
0
        ContextInitializer ()
232
0
        .silentHeaderParse (true)
233
0
        .strictHeaderValidation (false)
234
0
        .setInputStream (is),
235
0
        numThreads)
236
0
{
237
    // who uses this interface, can we remove it?
238
0
    _data->version = version;
239
0
    _data->header = header;
240
0
    _data->header_filled = true;
241
0
}
242
243
const char*
244
DeepScanLineInputFile::fileName () const
245
0
{
246
0
    return _ctxt.fileName ();
247
0
}
248
249
const Header&
250
DeepScanLineInputFile::header () const
251
2.41k
{
252
2.41k
#if ILMTHREAD_THREADING_ENABLED
253
2.41k
    std::lock_guard<std::mutex> lock (_data->_mx);
254
2.41k
#endif
255
2.41k
    if (!_data->header_filled)
256
1.08k
    {
257
1.08k
        _data->header = _ctxt.header (_data->partNumber);
258
1.08k
        _data->header_filled = true;
259
1.08k
    }
260
2.41k
    return _data->header;
261
2.41k
}
262
263
int
264
DeepScanLineInputFile::version () const
265
0
{
266
0
    return _data->version;
267
0
}
268
269
void
270
DeepScanLineInputFile::setFrameBuffer (const DeepFrameBuffer& frameBuffer)
271
1.32k
{
272
1.32k
#if ILMTHREAD_THREADING_ENABLED
273
1.32k
    std::lock_guard<std::mutex> lock (_data->_mx);
274
1.32k
#endif
275
1.32k
    _data->prepFillList (frameBuffer, _data->fill_list);
276
1.32k
    _data->frameBuffer = frameBuffer;
277
1.32k
    _data->frameBufferValid = true;
278
1.32k
}
279
280
const DeepFrameBuffer&
281
DeepScanLineInputFile::frameBuffer () const
282
0
{
283
0
#if ILMTHREAD_THREADING_ENABLED
284
0
    std::lock_guard<std::mutex> lock (_data->_mx);
285
0
#endif
286
0
    return _data->frameBuffer;
287
0
}
288
289
bool
290
DeepScanLineInputFile::isComplete () const
291
0
{
292
0
    return _ctxt.chunkTableValid (_data->partNumber);
293
0
}
294
295
void
296
DeepScanLineInputFile::readPixels (int scanLine1, int scanLine2)
297
593
{
298
593
    if (!_data->frameBufferValid)
299
0
    {
300
0
        throw IEX_NAMESPACE::ArgExc (
301
0
            "readPixels called with no valid frame buffer");
302
0
    }
303
304
593
    _data->readData (_data->frameBuffer, scanLine1, scanLine2, false);
305
593
}
306
307
void
308
DeepScanLineInputFile::readPixels (int scanLine)
309
0
{
310
0
    readPixels (scanLine, scanLine);
311
0
}
312
313
#pragma pack(push, 1)
314
struct DeepChunkHeader
315
{
316
    int32_t scanline;
317
    uint64_t packedCountSize;
318
    uint64_t packedDataSize;
319
    uint64_t unpackedDataSize;
320
};
321
#pragma pack(pop)
322
323
void
324
DeepScanLineInputFile::rawPixelData (
325
    int firstScanLine, char* pixelData, uint64_t& pixelDataSize)
326
0
{
327
0
    exr_chunk_info_t cinfo;
328
329
0
    static_assert (sizeof(DeepChunkHeader) == 28, "Expect a 28-byte chunk header");
330
331
    // api is kind of different than the normal scanline raw pixel data
332
    // in that it also includes the chunk header block, so the full chunk
333
334
0
    if (EXR_ERR_SUCCESS == exr_read_scanline_chunk_info (
335
0
            _ctxt, _data->partNumber, firstScanLine, &cinfo))
336
0
    {
337
0
        uint64_t cbytes;
338
0
        cbytes = sizeof (DeepChunkHeader);
339
0
        cbytes += cinfo.sample_count_table_size;
340
0
        cbytes += cinfo.packed_size;
341
342
0
        if (!pixelData || cbytes > pixelDataSize)
343
0
        {
344
0
            pixelDataSize = cbytes;
345
0
            return;
346
0
        }
347
348
0
        pixelDataSize = cbytes;
349
350
0
        DeepChunkHeader* dch = reinterpret_cast<DeepChunkHeader*> (pixelData);
351
0
        dch->scanline = cinfo.start_y;
352
0
        dch->packedCountSize = cinfo.sample_count_table_size;
353
0
        dch->packedDataSize = cinfo.packed_size;
354
0
        dch->unpackedDataSize = cinfo.unpacked_size;
355
356
0
        pixelData += sizeof(DeepChunkHeader);
357
0
        if (EXR_ERR_SUCCESS !=
358
0
            exr_read_deep_chunk (
359
0
                _ctxt,
360
0
                _data->partNumber,
361
0
                &cinfo,
362
0
                pixelData + cinfo.sample_count_table_size,
363
0
                pixelData))
364
0
        {
365
0
            THROW (
366
0
                IEX_NAMESPACE::ArgExc,
367
0
                "Error reading deep pixel data from image "
368
0
                "file \""
369
0
                << fileName () << "\". Unable to read raw pixel data of "
370
0
                << pixelDataSize << " bytes.");
371
0
        }
372
0
    }
373
0
    else
374
0
    {
375
0
        THROW (
376
0
            IEX_NAMESPACE::ArgExc,
377
0
            "Error reading deep pixel data from image "
378
0
            "file \""
379
0
            << fileName ()
380
0
            << "\". Unable to query data block information.");
381
0
    }
382
0
}
383
384
void
385
DeepScanLineInputFile::readPixels (
386
    const char*            rawPixelData,
387
    const DeepFrameBuffer& frameBuffer,
388
    int                    scanLine1,
389
    int                    scanLine2) const
390
0
{
391
0
    _data->readMemData (frameBuffer, rawPixelData, scanLine1, scanLine2, false);
392
0
}
393
394
void
395
DeepScanLineInputFile::readPixelSampleCounts (
396
    const char*            rawPixelData,
397
    const DeepFrameBuffer& frameBuffer,
398
    int                    scanLine1,
399
    int                    scanLine2) const
400
0
{
401
0
    _data->readMemData (frameBuffer, rawPixelData, scanLine1, scanLine2, true);
402
0
}
403
404
void
405
DeepScanLineInputFile::readPixelSampleCounts (int scanline1, int scanline2)
406
1.32k
{
407
1.32k
    if (!_data->frameBufferValid)
408
0
    {
409
0
        throw IEX_NAMESPACE::ArgExc (
410
0
            "readPixelSampleCounts called with no valid frame buffer");
411
0
    }
412
413
1.32k
    _data->readData (_data->frameBuffer, scanline1, scanline2, true);
414
1.32k
}
415
416
void
417
DeepScanLineInputFile::readPixelSampleCounts (int scanline)
418
0
{
419
0
    readPixelSampleCounts (scanline, scanline);
420
0
}
421
422
int
423
DeepScanLineInputFile::firstScanLineInChunk (int y) const
424
0
{
425
0
    return _data->getChunkRange (y).first;
426
0
}
427
428
int
429
DeepScanLineInputFile::lastScanLineInChunk (int y) const
430
0
{
431
0
    return _data->getChunkRange (y).second;
432
0
}
433
434
std::pair<int, int> DeepScanLineInputFile::Data::getChunkRange (int y) const
435
0
{
436
0
    exr_attr_box2i_t dw = _ctxt->dataWindow (partNumber);
437
0
    int32_t scansperchunk = 1;
438
439
0
    if (y < dw.min.y || y > dw.max.y)
440
0
    {
441
0
        THROW (
442
0
            IEX_NAMESPACE::ArgExc,
443
0
            "Requested scanline " << y << " is outside "
444
0
            "the image file's data window: "
445
0
            << dw.min.y << " - " << dw.max.y);
446
0
    }
447
448
0
    if (EXR_ERR_SUCCESS != exr_get_scanlines_per_chunk (*_ctxt, partNumber, &scansperchunk))
449
0
    {
450
0
        THROW (
451
0
            IEX_NAMESPACE::ArgExc,
452
0
            "Error querying scanline counts from image "
453
0
            "file \"" << _ctxt->fileName () << "\".");
454
0
    }
455
456
0
    if (scansperchunk == 1)
457
0
        return std::make_pair( y, y );
458
459
0
    std::pair<int, int> ret;
460
0
    int64_t yoff;
461
462
0
    yoff = (int64_t) y;
463
0
    yoff -= (int64_t) dw.min.y;
464
0
    yoff /= (int64_t) scansperchunk;
465
466
0
    yoff *= (int64_t) scansperchunk;
467
0
    yoff += (int64_t) dw.min.y;
468
469
0
    ret.first = (int32_t) yoff;
470
0
    yoff += (int64_t) scansperchunk;
471
0
    yoff = std::min (yoff, (int64_t) dw.max.y);
472
0
    ret.second = (int32_t) yoff;
473
474
0
    return ret;
475
0
}
476
477
void
478
DeepScanLineInputFile::Data::readData (
479
    const DeepFrameBuffer &fb, int scanLine1, int scanLine2, bool countsOnly)
480
1.91k
{
481
1.91k
    exr_attr_box2i_t dw = _ctxt->dataWindow (partNumber);
482
1.91k
    exr_chunk_info_t cinfo;
483
1.91k
    int32_t          scansperchunk = 1;
484
485
1.91k
    if (EXR_ERR_SUCCESS != exr_get_scanlines_per_chunk (*_ctxt, partNumber, &scansperchunk))
486
0
    {
487
0
        THROW (
488
0
            IEX_NAMESPACE::ArgExc,
489
0
            "Error querying scanline counts from image "
490
0
            "file \"" << _ctxt->fileName () << "\".");
491
0
    }
492
493
1.91k
    if (scanLine2 < scanLine1)
494
0
        std::swap (scanLine1, scanLine2);
495
496
1.91k
    if (scanLine1 < dw.min.y || scanLine2 > dw.max.y)
497
23
    {
498
23
        THROW (
499
23
            IEX_NAMESPACE::ArgExc,
500
23
            "Tried to read scan line outside "
501
23
            "the image file's data window: "
502
23
            << scanLine1 << " - " << scanLine2
503
23
            << " vs datawindow "
504
23
            << dw.min.y << " - " << dw.max.y);
505
23
    }
506
507
1.89k
#if ILMTHREAD_THREADING_ENABLED
508
1.89k
    int64_t nchunks;
509
1.89k
    nchunks = ((int64_t) scanLine2 - (int64_t) scanLine1);
510
1.89k
    nchunks /= (int64_t) scansperchunk;
511
1.89k
    nchunks += 1;
512
513
1.89k
    if (nchunks > 1 && numThreads > 1)
514
592
    {
515
        // we need the lifetime of this to last longer than the
516
        // lifetime of the task group below such that we don't get use
517
        // after free type error, so use scope rules to accomplish
518
        // this
519
592
        ScanLineProcessGroup sg (numThreads);
520
521
592
        {
522
592
            ILMTHREAD_NAMESPACE::TaskGroup tg;
523
524
640
            for (int y = scanLine1; y <= scanLine2; )
525
640
            {
526
640
                if (EXR_ERR_SUCCESS != exr_read_scanline_chunk_info (*_ctxt, partNumber, y, &cinfo))
527
592
                    throw IEX_NAMESPACE::InputExc ("Unable to query scanline information");
528
529
48
                ILMTHREAD_NAMESPACE::ThreadPool::addGlobalTask (
530
48
                    new LineBufferTask (&tg, this, &sg, &fb, cinfo, y, scanLine2, countsOnly) );
531
532
48
                y += scansperchunk - (y - cinfo.start_y);
533
48
            }
534
592
        }
535
536
0
        sg.throw_on_failure ();
537
0
    }
538
1.30k
    else
539
1.30k
#endif
540
1.30k
    {
541
1.30k
        ScanLineProcess sp;
542
1.30k
        bool redo = true;
543
544
1.30k
        sp.counts_only = countsOnly;
545
2.52k
        for (int y = scanLine1; y <= scanLine2; )
546
1.30k
        {
547
1.30k
            if (EXR_ERR_SUCCESS != exr_read_scanline_chunk_info (*_ctxt, partNumber, y, &cinfo))
548
84
                throw IEX_NAMESPACE::InputExc ("Unable to query scanline information");
549
550
            // Check if we have the same chunk where we can just
551
            // re-run the unpack (i.e. people reading 1 scan at a time
552
            // in a multi-scanline chunk)
553
1.21k
            if (!redo &&
554
0
                sp.cinfo.idx == cinfo.idx &&
555
0
                sp.last_decode_err == EXR_ERR_SUCCESS)
556
0
            {
557
0
                sp.run_unpack (
558
0
                    *_ctxt,
559
0
                    partNumber,
560
0
                    &fb,
561
0
                    y,
562
0
                    scanLine2,
563
0
                    fill_list);
564
0
            }
565
1.21k
            else
566
1.21k
            {
567
1.21k
                sp.cinfo = cinfo;
568
1.21k
                sp.run_decode (
569
1.21k
                    *_ctxt,
570
1.21k
                    partNumber,
571
1.21k
                    &fb,
572
1.21k
                    y,
573
1.21k
                    scanLine2,
574
1.21k
                    fill_list);
575
1.21k
                redo = false;
576
1.21k
            }
577
578
1.21k
            y += scansperchunk - (y - cinfo.start_y);
579
1.21k
        }
580
1.30k
    }
581
1.89k
}
582
583
////////////////////////////////////////
584
585
void
586
DeepScanLineInputFile::Data::readMemData (
587
        const DeepFrameBuffer &fb,
588
        const char *rawPixelData,
589
        int scanLine1,
590
        int scanLine2,
591
        bool countsOnly)
592
0
{
593
0
    const DeepChunkHeader* dch = reinterpret_cast<const DeepChunkHeader*> (rawPixelData);
594
0
    std::pair<int, int> range = getChunkRange (scanLine1);
595
596
0
    if (dch->scanline != scanLine1)
597
0
    {
598
0
        THROW (
599
0
            IEX_NAMESPACE::ArgExc,
600
0
            "readPixelSampleCounts(rawPixelData,frameBuffer,"
601
0
                << scanLine1 << ',' << scanLine2
602
0
                << ") called with incorrect start scanline - should be "
603
0
                << dch->scanline);
604
0
    }
605
606
0
    if (scanLine2 != range.second)
607
0
    {
608
0
        THROW (
609
0
            IEX_NAMESPACE::ArgExc,
610
0
            "readPixelSampleCounts(rawPixelData,frameBuffer,"
611
0
                << scanLine1 << ',' << scanLine2
612
0
                << ") called with incorrect end scanline - should be " << range.second);
613
0
    }
614
615
0
    rawPixelData += sizeof(DeepChunkHeader);
616
617
0
    std::vector<DeepSlice> fills;
618
0
    ScanLineProcess proc;
619
620
0
    if (countsOnly)
621
0
    {
622
0
        const Slice& scslice = fb.getSampleCountSlice ();
623
0
        if (!scslice.base)
624
0
        {
625
0
            throw IEX_NAMESPACE::ArgExc (
626
0
                "Invalid base pointer, please set a proper sample count slice.");
627
0
        }
628
0
        if (scslice.type != OPENEXR_IMF_INTERNAL_NAMESPACE::UINT)
629
0
        {
630
0
            throw IEX_NAMESPACE::ArgExc (
631
0
                "The type of sample count slice should be UINT.");
632
0
        }
633
0
    }
634
635
0
    if (!countsOnly)
636
0
        prepFillList(fb, fills);
637
638
0
    if (EXR_ERR_SUCCESS != exr_read_scanline_chunk_info (
639
0
            *_ctxt, partNumber, scanLine1, &proc.cinfo))
640
0
        throw IEX_NAMESPACE::InputExc ("Unable to query scanline information");
641
642
0
    proc.counts_only = countsOnly;
643
0
    proc.run_mem_decode (
644
0
        *_ctxt,
645
0
        partNumber,
646
0
        rawPixelData,
647
0
        &fb,
648
0
        scanLine1,
649
0
        scanLine2,
650
0
        fills);
651
0
}
652
653
////////////////////////////////////////
654
655
void DeepScanLineInputFile::Data::prepFillList (
656
    const DeepFrameBuffer &fb, std::vector<DeepSlice> &fill)
657
1.32k
{
658
1.32k
    const Slice& sampleCountSlice = fb.getSampleCountSlice ();
659
660
1.32k
    fill.clear ();
661
662
1.32k
    if (!sampleCountSlice.base)
663
0
    {
664
0
        throw IEX_NAMESPACE::ArgExc (
665
0
            "Invalid base pointer, please set a proper sample count slice.");
666
0
    }
667
668
7.40k
    for (DeepFrameBuffer::ConstIterator j = fb.begin (); j != fb.end (); ++j)
669
6.07k
    {
670
6.07k
        const exr_attr_chlist_entry_t* curc = _ctxt->findChannel (
671
6.07k
            partNumber, j.name ());
672
673
6.07k
        if (!curc)
674
2.87k
        {
675
2.87k
            fill.push_back (j.slice ());
676
2.87k
            continue;
677
2.87k
        }
678
679
3.20k
        if (curc->x_sampling != j.slice ().xSampling ||
680
3.20k
            curc->y_sampling != j.slice ().ySampling)
681
0
            THROW (
682
3.20k
                IEX_NAMESPACE::ArgExc,
683
3.20k
                "X and/or y subsampling factors "
684
3.20k
                "of \""
685
3.20k
                    << j.name ()
686
3.20k
                    << "\" channel "
687
3.20k
                       "of input file \""
688
3.20k
                    << _ctxt->fileName ()
689
3.20k
                    << "\" are "
690
3.20k
                       "not compatible with the frame buffer's "
691
3.20k
                       "subsampling factors.");
692
3.20k
    }
693
1.32k
}
694
695
696
////////////////////////////////////////
697
698
#if ILMTHREAD_THREADING_ENABLED
699
void DeepScanLineInputFile::Data::LineBufferTask::execute ()
700
48
{
701
48
    try
702
48
    {
703
48
        _line->run_decode (
704
48
            *(_ifd->_ctxt),
705
48
            _ifd->partNumber,
706
48
            _outfb,
707
48
            _fby,
708
48
            _last_fby,
709
48
            _ifd->fill_list);
710
48
    }
711
48
    catch (std::exception &e)
712
48
    {
713
11
        _line_group->record_failure (e.what ());
714
11
    }
715
48
    catch (...)
716
48
    {
717
0
        _line_group->record_failure ("Unknown exception");
718
0
    }
719
48
}
720
#endif
721
722
////////////////////////////////////////
723
724
static exr_result_t
725
mem_skip_read_chunk (exr_decode_pipeline_t* decode)
726
0
{
727
0
    return EXR_ERR_SUCCESS;
728
0
}
729
730
void ScanLineProcess::run_mem_decode (
731
        exr_const_context_t ctxt,
732
        int pn,
733
        const char *rawdata,
734
        const DeepFrameBuffer *outfb,
735
        int fbY,
736
        int fbLastY,
737
        const std::vector<DeepSlice> &filllist)
738
0
{
739
0
    if (!first)
740
0
        throw IEX_NAMESPACE::ArgExc ("Expect single-use process");
741
742
0
    if (EXR_ERR_SUCCESS != exr_decoding_initialize (ctxt, pn, &cinfo, &decoder))
743
0
        throw IEX_NAMESPACE::IoExc ("Unable to initialize decode pipeline");
744
745
    // Set first to false to allow destructor to deallocate any buffers
746
    // allocated during decoding.
747
0
    first = false;
748
749
0
    decoder.decode_flags |= EXR_DECODE_NON_IMAGE_DATA_AS_POINTERS;
750
0
    decoder.decode_flags |= EXR_DECODE_SAMPLE_COUNTS_AS_INDIVIDUAL;
751
0
    if (counts_only)
752
0
        decoder.decode_flags |= EXR_DECODE_SAMPLE_DATA_ONLY;
753
754
0
    update_pointers (outfb, fbY, fbLastY);
755
756
0
    if (EXR_ERR_SUCCESS !=
757
0
        exr_decoding_choose_default_routines (ctxt, pn, &decoder))
758
0
    {
759
0
        throw IEX_NAMESPACE::IoExc ("Unable to choose decoder routines");
760
0
    }
761
0
    decoder.read_fn = &mem_skip_read_chunk;
762
    // leave alloc sizes at 0 such that the decode pipeline doesn't
763
    // attempt to free, making it safe to cast const away
764
0
    decoder.packed_sample_count_table = const_cast<char*> (rawdata);
765
0
    rawdata += cinfo.sample_count_table_size;
766
0
    decoder.packed_buffer = const_cast<char*> (rawdata);
767
768
0
    last_decode_err = exr_decoding_run (ctxt, pn, &decoder);
769
0
    if (EXR_ERR_SUCCESS != last_decode_err)
770
0
        throw IEX_NAMESPACE::IoExc ("Unable to run decoder");
771
772
0
    copy_sample_count (outfb, fbY);
773
774
0
    if (counts_only)
775
0
        return;
776
777
0
    run_fill (outfb, fbY, filllist);
778
0
}
779
780
////////////////////////////////////////
781
782
void ScanLineProcess::run_decode (
783
    exr_const_context_t ctxt,
784
    int pn,
785
    const DeepFrameBuffer *outfb,
786
    int fbY,
787
    int fbLastY,
788
    const std::vector<DeepSlice> &filllist)
789
1.26k
{
790
1.26k
    last_decode_err = EXR_ERR_UNKNOWN;
791
1.26k
    uint8_t flags;
792
793
1.26k
    if (first)
794
1.26k
    {
795
1.26k
        if (EXR_ERR_SUCCESS !=
796
1.26k
            exr_decoding_initialize (ctxt, pn, &cinfo, &decoder))
797
1
        {
798
1
            throw IEX_NAMESPACE::IoExc ("Unable to initialize decode pipeline");
799
1
        }
800
1.26k
        decoder.decode_flags |= EXR_DECODE_NON_IMAGE_DATA_AS_POINTERS;
801
1.26k
        decoder.decode_flags |= EXR_DECODE_SAMPLE_COUNTS_AS_INDIVIDUAL;
802
1.26k
        flags = 0;
803
1.26k
        first = false;
804
1.26k
    }
805
0
    else
806
0
    {
807
0
        if (EXR_ERR_SUCCESS !=
808
0
            exr_decoding_update (ctxt, pn, &cinfo, &decoder))
809
0
        {
810
0
            throw IEX_NAMESPACE::IoExc ("Unable to update decode pipeline");
811
0
        }
812
0
        flags = decoder.decode_flags;
813
0
    }
814
815
1.26k
    if (counts_only)
816
672
        decoder.decode_flags |= EXR_DECODE_SAMPLE_DATA_ONLY;
817
593
    else
818
593
        decoder.decode_flags = decoder.decode_flags & ~EXR_DECODE_SAMPLE_DATA_ONLY;
819
820
1.26k
    update_pointers (outfb, fbY, fbLastY);
821
822
1.26k
    if (flags != decoder.decode_flags)
823
1.26k
    {
824
1.26k
        if (EXR_ERR_SUCCESS !=
825
1.26k
            exr_decoding_choose_default_routines (ctxt, pn, &decoder))
826
0
        {
827
0
            throw IEX_NAMESPACE::IoExc ("Unable to choose decoder routines");
828
0
        }
829
1.26k
    }
830
831
1.26k
    last_decode_err = exr_decoding_run (ctxt, pn, &decoder);
832
1.26k
    if (EXR_ERR_SUCCESS != last_decode_err)
833
77
        throw IEX_NAMESPACE::IoExc ("Unable to run decoder");
834
835
1.18k
    copy_sample_count (outfb, fbY);
836
837
1.18k
    if (counts_only)
838
630
        return;
839
840
558
    run_fill (outfb, fbY, filllist);
841
558
}
842
843
////////////////////////////////////////
844
845
void ScanLineProcess::run_unpack (
846
    exr_const_context_t ctxt,
847
    int pn,
848
    const DeepFrameBuffer *outfb,
849
    int fbY,
850
    int fbLastY,
851
    const std::vector<DeepSlice> &filllist)
852
0
{
853
0
    update_pointers (outfb, fbY, fbLastY);
854
855
0
    copy_sample_count (outfb, fbY);
856
857
0
    if (counts_only)
858
0
        return;
859
860
    /* won't work for deep where we need to re-allocate the number of
861
     * samples but for scenario where we have separated sample count read
862
     * and deep sample alloc, should be fine to bypass pipe
863
     * and run the unpacker */
864
0
    if (decoder.chunk.unpacked_size > 0 && decoder.unpack_and_convert_fn)
865
0
    {
866
0
        last_decode_err = decoder.unpack_and_convert_fn (&decoder);
867
0
        if (EXR_ERR_SUCCESS != last_decode_err)
868
0
            throw IEX_NAMESPACE::IoExc ("Unable to run decoder");
869
0
    }
870
871
0
    run_fill (outfb, fbY, filllist);
872
0
}
873
874
////////////////////////////////////////
875
876
void ScanLineProcess::update_pointers (
877
    const DeepFrameBuffer *outfb, int fbY, int fbLastY)
878
1.26k
{
879
1.26k
    decoder.user_line_begin_skip = fbY - cinfo.start_y;
880
1.26k
    decoder.user_line_end_ignore = 0;
881
1.26k
    int64_t endY = (int64_t)cinfo.start_y + (int64_t)cinfo.height - 1;
882
1.26k
    if ((int64_t)fbLastY < endY)
883
0
        decoder.user_line_end_ignore = (int32_t)(endY - fbLastY);
884
885
1.26k
    if (counts_only)
886
672
        return;
887
888
5.62k
    for (int c = 0; c < decoder.channel_count; ++c)
889
5.02k
    {
890
5.02k
        exr_coding_channel_info_t& curchan = decoder.channels[c];
891
5.02k
        uint8_t*                   ptr;
892
5.02k
        const DeepSlice*           fbslice;
893
894
5.02k
        fbslice = outfb->findSlice (curchan.channel_name);
895
896
5.02k
        if (curchan.height == 0 || !fbslice)
897
3.38k
        {
898
3.38k
            curchan.decode_to_ptr     = NULL;
899
3.38k
            curchan.user_pixel_stride = 0;
900
3.38k
            curchan.user_line_stride  = 0;
901
3.38k
            continue;
902
3.38k
        }
903
904
1.64k
        curchan.user_bytes_per_element = fbslice->sampleStride;
905
1.64k
        curchan.user_data_type         = (exr_pixel_type_t)fbslice->type;
906
1.64k
        curchan.user_pixel_stride      = fbslice->xStride;
907
1.64k
        curchan.user_line_stride       = fbslice->yStride;
908
909
1.64k
        ptr  = reinterpret_cast<uint8_t*> (fbslice->base);
910
1.64k
        ptr += int64_t (cinfo.start_x) * int64_t (fbslice->xStride);
911
1.64k
        ptr += int64_t (fbY) * int64_t (fbslice->yStride);
912
913
1.64k
        curchan.decode_to_ptr = ptr;
914
1.64k
    }
915
593
}
916
917
////////////////////////////////////////
918
919
void ScanLineProcess::run_fill (
920
    const DeepFrameBuffer *outfb,
921
    int fbY,
922
    const std::vector<DeepSlice> &filllist)
923
558
{
924
558
    for (auto& fills: filllist)
925
164
    {
926
164
        uint8_t*       ptr;
927
928
164
        if (fills.xSampling != 1 || fills.ySampling != 1)
929
0
            throw IEX_NAMESPACE::InputExc ("Expect sampling of 1");
930
931
164
        ptr  = reinterpret_cast<uint8_t*> (fills.base);
932
164
        ptr += int64_t (cinfo.start_x) * int64_t (fills.xStride);
933
164
        ptr += int64_t (fbY) * int64_t (fills.yStride);
934
935
        // TODO: update ImfMisc, lift fill type / value
936
164
        int stop = cinfo.start_y + cinfo.height - decoder.user_line_end_ignore;
937
328
        for ( int y = fbY; y < stop; ++y )
938
164
        {
939
164
            const int32_t* counts = decoder.sample_count_table;
940
164
            counts += ((int64_t) y - (int64_t) cinfo.start_y) * (int64_t) cinfo.width;
941
942
164
            uint8_t* outptr = ptr;
943
2.41M
            for ( int sx = 0, ex = cinfo.width; sx < ex; ++sx )
944
2.41M
            {
945
2.41M
                int32_t samps = counts[sx];
946
2.41M
                void *dest = *((void **)outptr);
947
948
2.41M
                if (samps == 0 || dest == nullptr)
949
2.41M
                {
950
2.41M
                    outptr += fills.xStride;
951
2.41M
                    continue;
952
2.41M
                }
953
954
0
                switch (fills.type)
955
0
                {
956
0
                    case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT:
957
0
                    {
958
0
                        unsigned int fillVal = (unsigned int) (fills.fillValue);
959
0
                        unsigned int* fillptr = static_cast<unsigned int*> (dest);
960
961
0
                        for ( int32_t s = 0; s < samps; ++s )
962
0
                            fillptr[s] = fillVal;
963
0
                        break;
964
0
                    }
965
966
0
                    case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF:
967
0
                    {
968
0
                        half fillVal = half (fills.fillValue);
969
0
                        half* fillptr = static_cast<half*> (dest);
970
971
0
                        for ( int32_t s = 0; s < samps; ++s )
972
0
                            fillptr[s] = fillVal;
973
0
                        break;
974
0
                    }
975
976
0
                    case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT:
977
0
                    {
978
0
                        float fillVal = float (fills.fillValue);
979
0
                        float* fillptr = static_cast<float*> (dest);
980
981
0
                        for ( int32_t s = 0; s < samps; ++s )
982
0
                            fillptr[s] = fillVal;
983
0
                        break;
984
0
                    }
985
0
                    default:
986
0
                        throw IEX_NAMESPACE::ArgExc ("Unknown pixel data type.");
987
0
                }
988
0
                outptr += fills.xStride;
989
0
            }
990
991
164
            ptr += fills.yStride;
992
164
        }
993
164
    }
994
558
}
995
996
////////////////////////////////////////
997
998
void ScanLineProcess::copy_sample_count (
999
    const DeepFrameBuffer *outfb,
1000
    int fbY)
1001
1.18k
{
1002
1.18k
    const Slice& scslice = outfb->getSampleCountSlice ();
1003
1004
1.18k
    if (!scslice.base)
1005
0
    {
1006
0
        throw IEX_NAMESPACE::ArgExc (
1007
0
            "Invalid base pointer, please set a proper sample count slice.");
1008
0
    }
1009
1.18k
    if (scslice.type != OPENEXR_IMF_INTERNAL_NAMESPACE::UINT)
1010
0
    {
1011
0
        throw IEX_NAMESPACE::ArgExc (
1012
0
            "The type of sample count slice should be UINT.");
1013
0
    }
1014
1015
1.18k
    int     end = cinfo.height - decoder.user_line_end_ignore;
1016
1.18k
    int64_t xS = int64_t (scslice.xStride);
1017
1.18k
    int64_t yS = int64_t (scslice.yStride);
1018
1019
2.37k
    for ( int y = decoder.user_line_begin_skip; y < end; ++y )
1020
1.18k
    {
1021
1.18k
        const int32_t* counts = decoder.sample_count_table + y * cinfo.width;
1022
1.18k
        uint8_t*       ptr;
1023
1024
1.18k
        ptr = reinterpret_cast<uint8_t*> (scslice.base);
1025
1.18k
        ptr += int64_t (cinfo.start_x) * xS;
1026
        /* y is chunk-relative (same indexing as sample_count_table rows);
1027
         * fbY is the first requested absolute line and may be mid-chunk. 
1028
         This allows for multi-scanline deep codecs to work. */
1029
1.18k
        ptr += (int64_t (cinfo.start_y) + int64_t (y)) * yS;
1030
1031
1.18k
        if (xS == sizeof(int32_t))
1032
1.18k
        {
1033
1.18k
            memcpy (ptr, counts, cinfo.width * sizeof(int32_t));
1034
1.18k
        }
1035
0
        else
1036
0
        {
1037
0
            for ( int x = 0; x < cinfo.width; ++x )
1038
0
            {
1039
0
                *((int32_t *)ptr) = counts[x];
1040
0
                ptr += xS;
1041
0
            }
1042
0
        }
1043
1.18k
    }
1044
1.18k
}
1045
1046
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT