Coverage Report

Created: 2025-08-29 06:25

/src/openexr/src/lib/OpenEXR/ImfDeepScanLineInputFile.cpp
Line
Count
Source (jump to first uncovered line)
1
//
2
// SPDX-License-Identifier: BSD-3-Clause
3
// Copyright (c) Contributors to the OpenEXR Project.
4
//
5
6
//-----------------------------------------------------------------------------
7
//
8
//      class 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
23.8k
    {
40
23.8k
        if (!first)
41
3.08k
            exr_decoding_destroy (decoder.context, &decoder);
42
23.8k
    }
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;
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
207k
    : _ctxt (ctxt)
102
207k
    , partNumber (pN)
103
207k
    , numThreads (nT)
104
207k
    {}
105
106
    void initialize ()
107
207k
    {
108
207k
        if (_ctxt->storage (partNumber) != EXR_STORAGE_DEEP_SCANLINE)
109
172k
            throw IEX_NAMESPACE::ArgExc ("File part is not a deep scanline part");
110
111
35.2k
        version = _ctxt->version ();
112
35.2k
    }
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
0
            : Task (group)
153
0
            , _outfb (outfb)
154
0
            , _ifd (ifd)
155
0
            , _fby (fby)
156
0
            , _last_fby (endScan)
157
0
            , _line (lineg->pop ())
158
0
            , _line_group (lineg)
159
0
        {
160
0
            _line->cinfo = cinfo;
161
0
            _line->counts_only = countsOnly;
162
0
        }
163
164
        ~LineBufferTask () override
165
0
        {
166
0
            _line_group->push (_line);
167
0
        }
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
207k
    : _ctxt (part->context),
186
207k
      _data (std::make_shared<Data> (&_ctxt, part->partNumber, part->numThreads))
187
207k
{
188
207k
    _data->initialize ();
189
207k
}
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
57.1k
{
252
57.1k
#if ILMTHREAD_THREADING_ENABLED
253
57.1k
    std::lock_guard<std::mutex> lock (_data->_mx);
254
57.1k
#endif
255
57.1k
    if (!_data->header_filled)
256
35.2k
    {
257
35.2k
        _data->header = _ctxt.header (_data->partNumber);
258
35.2k
        _data->header_filled = true;
259
35.2k
    }
260
57.1k
    return _data->header;
261
57.1k
}
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
21.9k
{
272
21.9k
#if ILMTHREAD_THREADING_ENABLED
273
21.9k
    std::lock_guard<std::mutex> lock (_data->_mx);
274
21.9k
#endif
275
21.9k
    _data->prepFillList (frameBuffer, _data->fill_list);
276
21.9k
    _data->frameBuffer = frameBuffer;
277
21.9k
    _data->frameBufferValid = true;
278
21.9k
}
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
1.36k
{
298
1.36k
    if (!_data->frameBufferValid)
299
0
    {
300
0
        throw IEX_NAMESPACE::ArgExc (
301
0
            "readPixels called with no valid frame buffer");
302
0
    }
303
304
1.36k
    _data->readData (_data->frameBuffer, scanLine1, scanLine2, false);
305
1.36k
}
306
307
void
308
DeepScanLineInputFile::readPixels (int scanLine)
309
733
{
310
733
    readPixels (scanLine, scanLine);
311
733
}
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
22.4k
{
407
22.4k
    if (!_data->frameBufferValid)
408
0
    {
409
0
        throw IEX_NAMESPACE::ArgExc (
410
0
            "readPixelSampleCounts called with no valid frame buffer");
411
0
    }
412
413
22.4k
    _data->readData (_data->frameBuffer, scanline1, scanline2, true);
414
22.4k
}
415
416
void
417
DeepScanLineInputFile::readPixelSampleCounts (int scanline)
418
18.1k
{
419
18.1k
    readPixelSampleCounts (scanline, scanline);
420
18.1k
}
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
23.8k
{
481
23.8k
    exr_attr_box2i_t dw = _ctxt->dataWindow (partNumber);
482
23.8k
    exr_chunk_info_t cinfo;
483
23.8k
    int32_t          scansperchunk = 1;
484
485
23.8k
    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
23.8k
    if (scanLine2 < scanLine1)
494
0
        std::swap (scanLine1, scanLine2);
495
496
23.8k
    if (scanLine1 < dw.min.y || scanLine2 > dw.max.y)
497
0
    {
498
0
        THROW (
499
0
            IEX_NAMESPACE::ArgExc,
500
0
            "Tried to read scan line outside "
501
0
            "the image file's data window: "
502
0
            << scanLine1 << " - " << scanLine2
503
0
            << " vs datawindow "
504
0
            << dw.min.y << " - " << dw.max.y);
505
0
    }
506
507
23.8k
#if ILMTHREAD_THREADING_ENABLED
508
23.8k
    int64_t nchunks;
509
23.8k
    nchunks = ((int64_t) scanLine2 - (int64_t) scanLine1);
510
23.8k
    nchunks /= (int64_t) scansperchunk;
511
23.8k
    nchunks += 1;
512
513
23.8k
    if (nchunks > 1 && numThreads > 1)
514
0
    {
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
0
        ScanLineProcessGroup sg (numThreads);
520
521
0
        {
522
0
            ILMTHREAD_NAMESPACE::TaskGroup tg;
523
524
0
            for (int y = scanLine1; y <= scanLine2; )
525
0
            {
526
0
                if (EXR_ERR_SUCCESS != exr_read_scanline_chunk_info (*_ctxt, partNumber, y, &cinfo))
527
0
                    throw IEX_NAMESPACE::InputExc ("Unable to query scanline information");
528
529
0
                ILMTHREAD_NAMESPACE::ThreadPool::addGlobalTask (
530
0
                    new LineBufferTask (&tg, this, &sg, &fb, cinfo, y, scanLine2, countsOnly) );
531
532
0
                y += scansperchunk - (y - cinfo.start_y);
533
0
            }
534
0
        }
535
536
0
        sg.throw_on_failure ();
537
0
    }
538
23.8k
    else
539
23.8k
#endif
540
23.8k
    {
541
23.8k
        ScanLineProcess sp;
542
23.8k
        bool redo = true;
543
544
23.8k
        sp.counts_only = countsOnly;
545
26.8k
        for (int y = scanLine1; y <= scanLine2; )
546
23.8k
        {
547
23.8k
            if (EXR_ERR_SUCCESS != exr_read_scanline_chunk_info (*_ctxt, partNumber, y, &cinfo))
548
20.7k
                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
3.08k
            if (!redo &&
554
3.08k
                sp.cinfo.idx == cinfo.idx &&
555
3.08k
                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
3.08k
            else
566
3.08k
            {
567
3.08k
                sp.cinfo = cinfo;
568
3.08k
                sp.run_decode (
569
3.08k
                    *_ctxt,
570
3.08k
                    partNumber,
571
3.08k
                    &fb,
572
3.08k
                    y,
573
3.08k
                    scanLine2,
574
3.08k
                    fill_list);
575
3.08k
                redo = false;
576
3.08k
            }
577
578
3.08k
            y += scansperchunk - (y - cinfo.start_y);
579
3.08k
        }
580
23.8k
    }
581
23.8k
}
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
        prepFillList(fb, fills);
622
623
0
    if (EXR_ERR_SUCCESS != exr_read_scanline_chunk_info (
624
0
            *_ctxt, partNumber, scanLine1, &proc.cinfo))
625
0
        throw IEX_NAMESPACE::InputExc ("Unable to query scanline information");
626
627
0
    proc.counts_only = countsOnly;
628
0
    proc.run_mem_decode (
629
0
        *_ctxt,
630
0
        partNumber,
631
0
        rawPixelData,
632
0
        &fb,
633
0
        scanLine1,
634
0
        scanLine2,
635
0
        fills);
636
0
}
637
638
////////////////////////////////////////
639
640
void DeepScanLineInputFile::Data::prepFillList (
641
    const DeepFrameBuffer &fb, std::vector<DeepSlice> &fill)
642
21.9k
{
643
21.9k
    const Slice& sampleCountSlice = fb.getSampleCountSlice ();
644
645
21.9k
    fill.clear ();
646
647
21.9k
    if (!sampleCountSlice.base)
648
0
    {
649
0
        throw IEX_NAMESPACE::ArgExc (
650
0
            "Invalid base pointer, please set a proper sample count slice.");
651
0
    }
652
653
113k
    for (DeepFrameBuffer::ConstIterator j = fb.begin (); j != fb.end (); ++j)
654
91.5k
    {
655
91.5k
        const exr_attr_chlist_entry_t* curc = _ctxt->findChannel (
656
91.5k
            partNumber, j.name ());
657
658
91.5k
        if (!curc)
659
48.8k
        {
660
48.8k
            fill.push_back (j.slice ());
661
48.8k
            continue;
662
48.8k
        }
663
664
42.6k
        if (curc->x_sampling != j.slice ().xSampling ||
665
42.6k
            curc->y_sampling != j.slice ().ySampling)
666
0
            THROW (
667
42.6k
                IEX_NAMESPACE::ArgExc,
668
42.6k
                "X and/or y subsampling factors "
669
42.6k
                "of \""
670
42.6k
                    << j.name ()
671
42.6k
                    << "\" channel "
672
42.6k
                       "of input file \""
673
42.6k
                    << _ctxt->fileName ()
674
42.6k
                    << "\" are "
675
42.6k
                       "not compatible with the frame buffer's "
676
42.6k
                       "subsampling factors.");
677
42.6k
    }
678
21.9k
}
679
680
681
////////////////////////////////////////
682
683
#if ILMTHREAD_THREADING_ENABLED
684
void DeepScanLineInputFile::Data::LineBufferTask::execute ()
685
0
{
686
0
    try
687
0
    {
688
0
        _line->run_decode (
689
0
            *(_ifd->_ctxt),
690
0
            _ifd->partNumber,
691
0
            _outfb,
692
0
            _fby,
693
0
            _last_fby,
694
0
            _ifd->fill_list);
695
0
    }
696
0
    catch (std::exception &e)
697
0
    {
698
0
        _line_group->record_failure (e.what ());
699
0
    }
700
0
    catch (...)
701
0
    {
702
0
        _line_group->record_failure ("Unknown exception");
703
0
    }
704
0
}
705
#endif
706
707
////////////////////////////////////////
708
709
static exr_result_t
710
mem_skip_read_chunk (exr_decode_pipeline_t* decode)
711
0
{
712
0
    return EXR_ERR_SUCCESS;
713
0
}
714
715
void ScanLineProcess::run_mem_decode (
716
        exr_const_context_t ctxt,
717
        int pn,
718
        const char *rawdata,
719
        const DeepFrameBuffer *outfb,
720
        int fbY,
721
        int fbLastY,
722
        const std::vector<DeepSlice> &filllist)
723
0
{
724
0
    if (!first)
725
0
        throw IEX_NAMESPACE::ArgExc ("Expect single-use process");
726
727
0
    if (EXR_ERR_SUCCESS != exr_decoding_initialize (ctxt, pn, &cinfo, &decoder))
728
0
        throw IEX_NAMESPACE::IoExc ("Unable to initialize decode pipeline");
729
730
    // Set first to false to allow destructor to deallocate any buffers
731
    // allocated during decoding.
732
0
    first = false;
733
734
0
    decoder.decode_flags |= EXR_DECODE_NON_IMAGE_DATA_AS_POINTERS;
735
0
    decoder.decode_flags |= EXR_DECODE_SAMPLE_COUNTS_AS_INDIVIDUAL;
736
0
    if (counts_only)
737
0
        decoder.decode_flags |= EXR_DECODE_SAMPLE_DATA_ONLY;
738
739
0
    update_pointers (outfb, fbY, fbLastY);
740
741
0
    if (EXR_ERR_SUCCESS !=
742
0
        exr_decoding_choose_default_routines (ctxt, pn, &decoder))
743
0
    {
744
0
        throw IEX_NAMESPACE::IoExc ("Unable to choose decoder routines");
745
0
    }
746
0
    decoder.read_fn = &mem_skip_read_chunk;
747
    // leave alloc sizes at 0 such that the decode pipeline doesn't
748
    // attempt to free, making it safe to cast const away
749
0
    decoder.packed_sample_count_table = const_cast<char*> (rawdata);
750
0
    rawdata += cinfo.sample_count_table_size;
751
0
    decoder.packed_buffer = const_cast<char*> (rawdata);
752
753
0
    last_decode_err = exr_decoding_run (ctxt, pn, &decoder);
754
0
    if (EXR_ERR_SUCCESS != last_decode_err)
755
0
        throw IEX_NAMESPACE::IoExc ("Unable to run decoder");
756
757
0
    copy_sample_count (outfb, fbY);
758
759
0
    if (counts_only)
760
0
        return;
761
762
0
    run_fill (outfb, fbY, filllist);
763
0
}
764
765
////////////////////////////////////////
766
767
void ScanLineProcess::run_decode (
768
    exr_const_context_t ctxt,
769
    int pn,
770
    const DeepFrameBuffer *outfb,
771
    int fbY,
772
    int fbLastY,
773
    const std::vector<DeepSlice> &filllist)
774
3.08k
{
775
3.08k
    last_decode_err = EXR_ERR_UNKNOWN;
776
3.08k
    uint8_t flags;
777
778
3.08k
    if (first)
779
3.08k
    {
780
3.08k
        if (EXR_ERR_SUCCESS !=
781
3.08k
            exr_decoding_initialize (ctxt, pn, &cinfo, &decoder))
782
3
        {
783
3
            throw IEX_NAMESPACE::IoExc ("Unable to initialize decode pipeline");
784
3
        }
785
3.08k
        decoder.decode_flags |= EXR_DECODE_NON_IMAGE_DATA_AS_POINTERS;
786
3.08k
        decoder.decode_flags |= EXR_DECODE_SAMPLE_COUNTS_AS_INDIVIDUAL;
787
3.08k
        flags = 0;
788
3.08k
        first = false;
789
3.08k
    }
790
0
    else
791
0
    {
792
0
        if (EXR_ERR_SUCCESS !=
793
0
            exr_decoding_update (ctxt, pn, &cinfo, &decoder))
794
0
        {
795
0
            throw IEX_NAMESPACE::IoExc ("Unable to update decode pipeline");
796
0
        }
797
0
        flags = decoder.decode_flags;
798
0
    }
799
800
3.08k
    if (counts_only)
801
1.72k
        decoder.decode_flags |= EXR_DECODE_SAMPLE_DATA_ONLY;
802
1.36k
    else
803
1.36k
        decoder.decode_flags = decoder.decode_flags & ~EXR_DECODE_SAMPLE_DATA_ONLY;
804
805
3.08k
    update_pointers (outfb, fbY, fbLastY);
806
807
3.08k
    if (flags != decoder.decode_flags)
808
3.08k
    {
809
3.08k
        if (EXR_ERR_SUCCESS !=
810
3.08k
            exr_decoding_choose_default_routines (ctxt, pn, &decoder))
811
0
        {
812
0
            throw IEX_NAMESPACE::IoExc ("Unable to choose decoder routines");
813
0
        }
814
3.08k
    }
815
816
3.08k
    last_decode_err = exr_decoding_run (ctxt, pn, &decoder);
817
3.08k
    if (EXR_ERR_SUCCESS != last_decode_err)
818
162
        throw IEX_NAMESPACE::IoExc ("Unable to run decoder");
819
820
2.92k
    copy_sample_count (outfb, fbY);
821
822
2.92k
    if (counts_only)
823
1.60k
        return;
824
825
1.31k
    run_fill (outfb, fbY, filllist);
826
1.31k
}
827
828
////////////////////////////////////////
829
830
void ScanLineProcess::run_unpack (
831
    exr_const_context_t ctxt,
832
    int pn,
833
    const DeepFrameBuffer *outfb,
834
    int fbY,
835
    int fbLastY,
836
    const std::vector<DeepSlice> &filllist)
837
0
{
838
0
    update_pointers (outfb, fbY, fbLastY);
839
840
0
    copy_sample_count (outfb, fbY);
841
842
0
    if (counts_only)
843
0
        return;
844
845
    /* won't work for deep where we need to re-allocate the number of
846
     * samples but for scenario where we have separated sample count read
847
     * and deep sample alloc, should be fine to bypass pipe
848
     * and run the unpacker */
849
0
    if (decoder.chunk.unpacked_size > 0 && decoder.unpack_and_convert_fn)
850
0
    {
851
0
        last_decode_err = decoder.unpack_and_convert_fn (&decoder);
852
0
        if (EXR_ERR_SUCCESS != last_decode_err)
853
0
            throw IEX_NAMESPACE::IoExc ("Unable to run decoder");
854
0
    }
855
856
0
    run_fill (outfb, fbY, filllist);
857
0
}
858
859
////////////////////////////////////////
860
861
void ScanLineProcess::update_pointers (
862
    const DeepFrameBuffer *outfb, int fbY, int fbLastY)
863
3.08k
{
864
3.08k
    decoder.user_line_begin_skip = fbY - cinfo.start_y;
865
3.08k
    decoder.user_line_end_ignore = 0;
866
3.08k
    int64_t endY = (int64_t)cinfo.start_y + (int64_t)cinfo.height - 1;
867
3.08k
    if ((int64_t)fbLastY < endY)
868
0
        decoder.user_line_end_ignore = (int32_t)(endY - fbLastY);
869
870
3.08k
    if (counts_only)
871
1.72k
        return;
872
873
8.61k
    for (int c = 0; c < decoder.channel_count; ++c)
874
7.25k
    {
875
7.25k
        exr_coding_channel_info_t& curchan = decoder.channels[c];
876
7.25k
        uint8_t*                   ptr;
877
7.25k
        const DeepSlice*           fbslice;
878
879
7.25k
        fbslice = outfb->findSlice (curchan.channel_name);
880
881
7.25k
        if (curchan.height == 0 || !fbslice)
882
0
        {
883
0
            curchan.decode_to_ptr     = NULL;
884
0
            curchan.user_pixel_stride = 0;
885
0
            curchan.user_line_stride  = 0;
886
0
            continue;
887
0
        }
888
889
7.25k
        curchan.user_bytes_per_element = fbslice->sampleStride;
890
7.25k
        curchan.user_data_type         = (exr_pixel_type_t)fbslice->type;
891
7.25k
        curchan.user_pixel_stride      = fbslice->xStride;
892
7.25k
        curchan.user_line_stride       = fbslice->yStride;
893
894
7.25k
        ptr  = reinterpret_cast<uint8_t*> (fbslice->base);
895
7.25k
        ptr += int64_t (cinfo.start_x) * int64_t (fbslice->xStride);
896
7.25k
        ptr += int64_t (fbY) * int64_t (fbslice->yStride);
897
898
7.25k
        curchan.decode_to_ptr = ptr;
899
7.25k
    }
900
1.36k
}
901
902
////////////////////////////////////////
903
904
void ScanLineProcess::run_fill (
905
    const DeepFrameBuffer *outfb,
906
    int fbY,
907
    const std::vector<DeepSlice> &filllist)
908
1.31k
{
909
1.31k
    for (auto& fills: filllist)
910
5.22k
    {
911
5.22k
        uint8_t*       ptr;
912
913
5.22k
        if (fills.xSampling != 1 || fills.ySampling != 1)
914
0
            throw IEX_NAMESPACE::InputExc ("Expect sampling of 1");
915
916
5.22k
        ptr  = reinterpret_cast<uint8_t*> (fills.base);
917
5.22k
        ptr += int64_t (cinfo.start_x) * int64_t (fills.xStride);
918
5.22k
        ptr += int64_t (fbY) * int64_t (fills.yStride);
919
920
        // TODO: update ImfMisc, lift fill type / value
921
5.22k
        int stop = cinfo.start_y + cinfo.height - decoder.user_line_end_ignore;
922
10.4k
        for ( int y = fbY; y < stop; ++y )
923
5.22k
        {
924
5.22k
            const int32_t* counts = decoder.sample_count_table;
925
5.22k
            counts += ((int64_t) y - (int64_t) cinfo.start_y) * (int64_t) cinfo.width;
926
927
5.22k
            uint8_t* outptr = ptr;
928
20.3k
            for ( int sx = 0, ex = cinfo.width; sx < ex; ++sx )
929
15.1k
            {
930
15.1k
                int32_t samps = counts[sx];
931
15.1k
                void *dest = *((void **)outptr);
932
933
15.1k
                if (samps == 0 || dest == nullptr)
934
7.57k
                {
935
7.57k
                    outptr += fills.xStride;
936
7.57k
                    continue;
937
7.57k
                }
938
939
7.57k
                switch (fills.type)
940
7.57k
                {
941
0
                    case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT:
942
0
                    {
943
0
                        unsigned int fillVal = (unsigned int) (fills.fillValue);
944
0
                        unsigned int* fillptr = static_cast<unsigned int*> (dest);
945
946
0
                        for ( int32_t s = 0; s < samps; ++s )
947
0
                            fillptr[s] = fillVal;
948
0
                        break;
949
0
                    }
950
951
0
                    case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF:
952
0
                    {
953
0
                        half fillVal = half (fills.fillValue);
954
0
                        half* fillptr = static_cast<half*> (dest);
955
956
0
                        for ( int32_t s = 0; s < samps; ++s )
957
0
                            fillptr[s] = fillVal;
958
0
                        break;
959
0
                    }
960
961
7.57k
                    case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT:
962
7.57k
                    {
963
7.57k
                        float fillVal = float (fills.fillValue);
964
7.57k
                        float* fillptr = static_cast<float*> (dest);
965
966
17.0M
                        for ( int32_t s = 0; s < samps; ++s )
967
17.0M
                            fillptr[s] = fillVal;
968
7.57k
                        break;
969
0
                    }
970
0
                    default:
971
0
                        throw IEX_NAMESPACE::ArgExc ("Unknown pixel data type.");
972
7.57k
                }
973
7.57k
                outptr += fills.xStride;
974
7.57k
            }
975
976
5.22k
            ptr += fills.yStride;
977
5.22k
        }
978
5.22k
    }
979
1.31k
}
980
981
////////////////////////////////////////
982
983
void ScanLineProcess::copy_sample_count (
984
    const DeepFrameBuffer *outfb,
985
    int fbY)
986
2.92k
{
987
2.92k
    const Slice& scslice = outfb->getSampleCountSlice ();
988
989
2.92k
    int     end = cinfo.height - decoder.user_line_end_ignore;
990
2.92k
    int64_t xS = int64_t (scslice.xStride);
991
2.92k
    int64_t yS = int64_t (scslice.yStride);
992
993
5.84k
    for ( int y = decoder.user_line_begin_skip; y < end; ++y )
994
2.92k
    {
995
2.92k
        const int32_t* counts = decoder.sample_count_table + y * cinfo.width;
996
2.92k
        uint8_t*       ptr;
997
998
2.92k
        ptr = reinterpret_cast<uint8_t*> (scslice.base);
999
2.92k
        ptr += int64_t (cinfo.start_x) * xS;
1000
2.92k
        ptr += (int64_t (fbY) + int64_t (y)) * yS;
1001
1002
2.92k
        if (xS == sizeof(int32_t))
1003
2.92k
        {
1004
2.92k
            memcpy (ptr, counts, cinfo.width * sizeof(int32_t));
1005
2.92k
        }
1006
0
        else
1007
0
        {
1008
0
            for ( int x = 0; x < cinfo.width; ++x )
1009
0
            {
1010
0
                *((int32_t *)ptr) = counts[x];
1011
0
                ptr += xS;
1012
0
            }
1013
0
        }
1014
2.92k
    }
1015
2.92k
}
1016
1017
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT