Coverage Report

Created: 2025-07-11 06:50

/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
22.6k
    {
40
22.6k
        if (!first)
41
2.41k
            exr_decoding_destroy (decoder.context, &decoder);
42
22.6k
    }
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
191k
    : _ctxt (ctxt)
102
191k
    , partNumber (pN)
103
191k
    , numThreads (nT)
104
191k
    {}
105
106
    void initialize ()
107
191k
    {
108
191k
        if (_ctxt->storage (partNumber) != EXR_STORAGE_DEEP_SCANLINE)
109
158k
            throw IEX_NAMESPACE::ArgExc ("File part is not a deep scanline part");
110
111
33.6k
        version = _ctxt->version ();
112
33.6k
    }
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
191k
    : _ctxt (part->context),
186
191k
      _data (std::make_shared<Data> (&_ctxt, part->partNumber, part->numThreads))
187
191k
{
188
191k
    _data->initialize ();
189
191k
}
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
54.9k
{
252
54.9k
#if ILMTHREAD_THREADING_ENABLED
253
54.9k
    std::lock_guard<std::mutex> lock (_data->_mx);
254
54.9k
#endif
255
54.9k
    if (!_data->header_filled)
256
33.6k
    {
257
33.6k
        _data->header = _ctxt.header (_data->partNumber);
258
33.6k
        _data->header_filled = true;
259
33.6k
    }
260
54.9k
    return _data->header;
261
54.9k
}
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.2k
{
272
21.2k
#if ILMTHREAD_THREADING_ENABLED
273
21.2k
    std::lock_guard<std::mutex> lock (_data->_mx);
274
21.2k
#endif
275
21.2k
    _data->prepFillList (frameBuffer, _data->fill_list);
276
21.2k
    _data->frameBuffer = frameBuffer;
277
21.2k
    _data->frameBufferValid = true;
278
21.2k
}
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.02k
{
298
1.02k
    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.02k
    _data->readData (_data->frameBuffer, scanLine1, scanLine2, false);
305
1.02k
}
306
307
void
308
DeepScanLineInputFile::readPixels (int scanLine)
309
504
{
310
504
    readPixels (scanLine, scanLine);
311
504
}
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
21.5k
{
407
21.5k
    if (!_data->frameBufferValid)
408
0
    {
409
0
        throw IEX_NAMESPACE::ArgExc (
410
0
            "readPixelSampleCounts called with no valid frame buffer");
411
0
    }
412
413
21.5k
    _data->readData (_data->frameBuffer, scanline1, scanline2, true);
414
21.5k
}
415
416
void
417
DeepScanLineInputFile::readPixelSampleCounts (int scanline)
418
17.1k
{
419
17.1k
    readPixelSampleCounts (scanline, scanline);
420
17.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
22.6k
{
481
22.6k
    exr_attr_box2i_t dw = _ctxt->dataWindow (partNumber);
482
22.6k
    exr_chunk_info_t cinfo;
483
22.6k
    int32_t          scansperchunk = 1;
484
485
22.6k
    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
22.6k
    if (scanLine2 < scanLine1)
494
0
        std::swap (scanLine1, scanLine2);
495
496
22.6k
    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
22.6k
#if ILMTHREAD_THREADING_ENABLED
508
22.6k
    int64_t nchunks;
509
22.6k
    nchunks = ((int64_t) scanLine2 - (int64_t) scanLine1);
510
22.6k
    nchunks /= (int64_t) scansperchunk;
511
22.6k
    nchunks += 1;
512
513
22.6k
    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
22.6k
    else
539
22.6k
#endif
540
22.6k
    {
541
22.6k
        ScanLineProcess sp;
542
22.6k
        bool redo = true;
543
544
22.6k
        sp.counts_only = countsOnly;
545
25.0k
        for (int y = scanLine1; y <= scanLine2; )
546
22.6k
        {
547
22.6k
            if (EXR_ERR_SUCCESS != exr_read_scanline_chunk_info (*_ctxt, partNumber, y, &cinfo))
548
20.1k
                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
2.43k
            if (!redo &&
554
2.43k
                sp.cinfo.idx == cinfo.idx &&
555
2.43k
                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
2.43k
            else
566
2.43k
            {
567
2.43k
                sp.cinfo = cinfo;
568
2.43k
                sp.run_decode (
569
2.43k
                    *_ctxt,
570
2.43k
                    partNumber,
571
2.43k
                    &fb,
572
2.43k
                    y,
573
2.43k
                    scanLine2,
574
2.43k
                    fill_list);
575
2.43k
                redo = false;
576
2.43k
            }
577
578
2.43k
            y += scansperchunk - (y - cinfo.start_y);
579
2.43k
        }
580
22.6k
    }
581
22.6k
}
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.2k
{
643
21.2k
    const Slice& sampleCountSlice = fb.getSampleCountSlice ();
644
645
21.2k
    fill.clear ();
646
647
21.2k
    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
103k
    for (DeepFrameBuffer::ConstIterator j = fb.begin (); j != fb.end (); ++j)
654
81.8k
    {
655
81.8k
        const exr_attr_chlist_entry_t* curc = _ctxt->findChannel (
656
81.8k
            partNumber, j.name ());
657
658
81.8k
        if (!curc)
659
38.7k
        {
660
38.7k
            fill.push_back (j.slice ());
661
38.7k
            continue;
662
38.7k
        }
663
664
43.0k
        if (curc->x_sampling != j.slice ().xSampling ||
665
43.0k
            curc->y_sampling != j.slice ().ySampling)
666
0
            THROW (
667
43.0k
                IEX_NAMESPACE::ArgExc,
668
43.0k
                "X and/or y subsampling factors "
669
43.0k
                "of \""
670
43.0k
                    << j.name ()
671
43.0k
                    << "\" channel "
672
43.0k
                       "of input file \""
673
43.0k
                    << _ctxt->fileName ()
674
43.0k
                    << "\" are "
675
43.0k
                       "not compatible with the frame buffer's "
676
43.0k
                       "subsampling factors.");
677
43.0k
    }
678
21.2k
}
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
0
    decoder.decode_flags |= EXR_DECODE_NON_IMAGE_DATA_AS_POINTERS;
731
0
    decoder.decode_flags |= EXR_DECODE_SAMPLE_COUNTS_AS_INDIVIDUAL;
732
0
    if (counts_only)
733
0
        decoder.decode_flags |= EXR_DECODE_SAMPLE_DATA_ONLY;
734
735
0
    update_pointers (outfb, fbY, fbLastY);
736
737
0
    if (EXR_ERR_SUCCESS !=
738
0
        exr_decoding_choose_default_routines (ctxt, pn, &decoder))
739
0
    {
740
0
        throw IEX_NAMESPACE::IoExc ("Unable to choose decoder routines");
741
0
    }
742
0
    decoder.read_fn = &mem_skip_read_chunk;
743
    // leave alloc sizes at 0 such that the decode pipeline doesn't
744
    // attempt to free, making it safe to cast const away
745
0
    decoder.packed_sample_count_table = const_cast<char*> (rawdata);
746
0
    rawdata += cinfo.sample_count_table_size;
747
0
    decoder.packed_buffer = const_cast<char*> (rawdata);
748
749
0
    last_decode_err = exr_decoding_run (ctxt, pn, &decoder);
750
0
    if (EXR_ERR_SUCCESS != last_decode_err)
751
0
        throw IEX_NAMESPACE::IoExc ("Unable to run decoder");
752
753
0
    copy_sample_count (outfb, fbY);
754
755
0
    if (counts_only)
756
0
        return;
757
758
0
    run_fill (outfb, fbY, filllist);
759
0
}
760
761
////////////////////////////////////////
762
763
void ScanLineProcess::run_decode (
764
    exr_const_context_t ctxt,
765
    int pn,
766
    const DeepFrameBuffer *outfb,
767
    int fbY,
768
    int fbLastY,
769
    const std::vector<DeepSlice> &filllist)
770
2.43k
{
771
2.43k
    last_decode_err = EXR_ERR_UNKNOWN;
772
2.43k
    uint8_t flags;
773
774
2.43k
    if (first)
775
2.43k
    {
776
2.43k
        if (EXR_ERR_SUCCESS !=
777
2.43k
            exr_decoding_initialize (ctxt, pn, &cinfo, &decoder))
778
21
        {
779
21
            throw IEX_NAMESPACE::IoExc ("Unable to initialize decode pipeline");
780
21
        }
781
2.41k
        decoder.decode_flags |= EXR_DECODE_NON_IMAGE_DATA_AS_POINTERS;
782
2.41k
        decoder.decode_flags |= EXR_DECODE_SAMPLE_COUNTS_AS_INDIVIDUAL;
783
2.41k
        flags = 0;
784
2.41k
        first = false;
785
2.41k
    }
786
0
    else
787
0
    {
788
0
        if (EXR_ERR_SUCCESS !=
789
0
            exr_decoding_update (ctxt, pn, &cinfo, &decoder))
790
0
        {
791
0
            throw IEX_NAMESPACE::IoExc ("Unable to update decode pipeline");
792
0
        }
793
0
        flags = decoder.decode_flags;
794
0
    }
795
796
2.41k
    if (counts_only)
797
1.39k
        decoder.decode_flags |= EXR_DECODE_SAMPLE_DATA_ONLY;
798
1.02k
    else
799
1.02k
        decoder.decode_flags = decoder.decode_flags & ~EXR_DECODE_SAMPLE_DATA_ONLY;
800
801
2.41k
    update_pointers (outfb, fbY, fbLastY);
802
803
2.41k
    if (flags != decoder.decode_flags)
804
2.41k
    {
805
2.41k
        if (EXR_ERR_SUCCESS !=
806
2.41k
            exr_decoding_choose_default_routines (ctxt, pn, &decoder))
807
0
        {
808
0
            throw IEX_NAMESPACE::IoExc ("Unable to choose decoder routines");
809
0
        }
810
2.41k
    }
811
812
2.41k
    last_decode_err = exr_decoding_run (ctxt, pn, &decoder);
813
2.41k
    if (EXR_ERR_SUCCESS != last_decode_err)
814
181
        throw IEX_NAMESPACE::IoExc ("Unable to run decoder");
815
816
2.23k
    copy_sample_count (outfb, fbY);
817
818
2.23k
    if (counts_only)
819
1.25k
        return;
820
821
982
    run_fill (outfb, fbY, filllist);
822
982
}
823
824
////////////////////////////////////////
825
826
void ScanLineProcess::run_unpack (
827
    exr_const_context_t ctxt,
828
    int pn,
829
    const DeepFrameBuffer *outfb,
830
    int fbY,
831
    int fbLastY,
832
    const std::vector<DeepSlice> &filllist)
833
0
{
834
0
    update_pointers (outfb, fbY, fbLastY);
835
836
0
    copy_sample_count (outfb, fbY);
837
838
0
    if (counts_only)
839
0
        return;
840
841
    /* won't work for deep where we need to re-allocate the number of
842
     * samples but for scenario where we have separated sample count read
843
     * and deep sample alloc, should be fine to bypass pipe
844
     * and run the unpacker */
845
0
    if (decoder.chunk.unpacked_size > 0 && decoder.unpack_and_convert_fn)
846
0
    {
847
0
        last_decode_err = decoder.unpack_and_convert_fn (&decoder);
848
0
        if (EXR_ERR_SUCCESS != last_decode_err)
849
0
            throw IEX_NAMESPACE::IoExc ("Unable to run decoder");
850
0
    }
851
852
0
    run_fill (outfb, fbY, filllist);
853
0
}
854
855
////////////////////////////////////////
856
857
void ScanLineProcess::update_pointers (
858
    const DeepFrameBuffer *outfb, int fbY, int fbLastY)
859
2.41k
{
860
2.41k
    decoder.user_line_begin_skip = fbY - cinfo.start_y;
861
2.41k
    decoder.user_line_end_ignore = 0;
862
2.41k
    int64_t endY = (int64_t)cinfo.start_y + (int64_t)cinfo.height - 1;
863
2.41k
    if ((int64_t)fbLastY < endY)
864
0
        decoder.user_line_end_ignore = (int32_t)(endY - fbLastY);
865
866
2.41k
    if (counts_only)
867
1.39k
        return;
868
869
6.21k
    for (int c = 0; c < decoder.channel_count; ++c)
870
5.19k
    {
871
5.19k
        exr_coding_channel_info_t& curchan = decoder.channels[c];
872
5.19k
        uint8_t*                   ptr;
873
5.19k
        const DeepSlice*           fbslice;
874
875
5.19k
        fbslice = outfb->findSlice (curchan.channel_name);
876
877
5.19k
        if (curchan.height == 0 || !fbslice)
878
0
        {
879
0
            curchan.decode_to_ptr     = NULL;
880
0
            curchan.user_pixel_stride = 0;
881
0
            curchan.user_line_stride  = 0;
882
0
            continue;
883
0
        }
884
885
5.19k
        curchan.user_bytes_per_element = fbslice->sampleStride;
886
5.19k
        curchan.user_data_type         = (exr_pixel_type_t)fbslice->type;
887
5.19k
        curchan.user_pixel_stride      = fbslice->xStride;
888
5.19k
        curchan.user_line_stride       = fbslice->yStride;
889
890
5.19k
        ptr  = reinterpret_cast<uint8_t*> (fbslice->base);
891
5.19k
        ptr += int64_t (cinfo.start_x) * int64_t (fbslice->xStride);
892
5.19k
        ptr += int64_t (fbY) * int64_t (fbslice->yStride);
893
894
5.19k
        curchan.decode_to_ptr = ptr;
895
5.19k
    }
896
1.02k
}
897
898
////////////////////////////////////////
899
900
void ScanLineProcess::run_fill (
901
    const DeepFrameBuffer *outfb,
902
    int fbY,
903
    const std::vector<DeepSlice> &filllist)
904
982
{
905
982
    for (auto& fills: filllist)
906
3.24k
    {
907
3.24k
        uint8_t*       ptr;
908
909
3.24k
        if (fills.xSampling != 1 || fills.ySampling != 1)
910
0
            throw IEX_NAMESPACE::InputExc ("Expect sampling of 1");
911
912
3.24k
        ptr  = reinterpret_cast<uint8_t*> (fills.base);
913
3.24k
        ptr += int64_t (cinfo.start_x) * int64_t (fills.xStride);
914
3.24k
        ptr += int64_t (fbY) * int64_t (fills.yStride);
915
916
        // TODO: update ImfMisc, lift fill type / value
917
3.24k
        int stop = cinfo.start_y + cinfo.height - decoder.user_line_end_ignore;
918
6.48k
        for ( int y = fbY; y < stop; ++y )
919
3.24k
        {
920
3.24k
            const int32_t* counts = decoder.sample_count_table;
921
3.24k
            counts += ((int64_t) y - (int64_t) cinfo.start_y) * (int64_t) cinfo.width;
922
923
3.24k
            uint8_t* outptr = ptr;
924
15.6k
            for ( int sx = 0, ex = cinfo.width; sx < ex; ++sx )
925
12.4k
            {
926
12.4k
                int32_t samps = counts[sx];
927
12.4k
                void *dest = *((void **)outptr);
928
929
12.4k
                if (samps == 0 || dest == nullptr)
930
8.10k
                {
931
8.10k
                    outptr += fills.xStride;
932
8.10k
                    continue;
933
8.10k
                }
934
935
4.33k
                switch (fills.type)
936
4.33k
                {
937
0
                    case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT:
938
0
                    {
939
0
                        unsigned int fillVal = (unsigned int) (fills.fillValue);
940
0
                        unsigned int* fillptr = static_cast<unsigned int*> (dest);
941
942
0
                        for ( int32_t s = 0; s < samps; ++s )
943
0
                            fillptr[s] = fillVal;
944
0
                        break;
945
0
                    }
946
947
0
                    case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF:
948
0
                    {
949
0
                        half fillVal = half (fills.fillValue);
950
0
                        half* fillptr = static_cast<half*> (dest);
951
952
0
                        for ( int32_t s = 0; s < samps; ++s )
953
0
                            fillptr[s] = fillVal;
954
0
                        break;
955
0
                    }
956
957
4.33k
                    case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT:
958
4.33k
                    {
959
4.33k
                        float fillVal = float (fills.fillValue);
960
4.33k
                        float* fillptr = static_cast<float*> (dest);
961
962
21.7M
                        for ( int32_t s = 0; s < samps; ++s )
963
21.7M
                            fillptr[s] = fillVal;
964
4.33k
                        break;
965
0
                    }
966
0
                    default:
967
0
                        throw IEX_NAMESPACE::ArgExc ("Unknown pixel data type.");
968
4.33k
                }
969
4.33k
                outptr += fills.xStride;
970
4.33k
            }
971
972
3.24k
            ptr += fills.yStride;
973
3.24k
        }
974
3.24k
    }
975
982
}
976
977
////////////////////////////////////////
978
979
void ScanLineProcess::copy_sample_count (
980
    const DeepFrameBuffer *outfb,
981
    int fbY)
982
2.23k
{
983
2.23k
    const Slice& scslice = outfb->getSampleCountSlice ();
984
985
2.23k
    int     end = cinfo.height - decoder.user_line_end_ignore;
986
2.23k
    int64_t xS = int64_t (scslice.xStride);
987
2.23k
    int64_t yS = int64_t (scslice.yStride);
988
989
4.47k
    for ( int y = decoder.user_line_begin_skip; y < end; ++y )
990
2.23k
    {
991
2.23k
        const int32_t* counts = decoder.sample_count_table + y * cinfo.width;
992
2.23k
        uint8_t*       ptr;
993
994
2.23k
        ptr = reinterpret_cast<uint8_t*> (scslice.base);
995
2.23k
        ptr += int64_t (cinfo.start_x) * xS;
996
2.23k
        ptr += (int64_t (fbY) + int64_t (y)) * yS;
997
998
2.23k
        if (xS == sizeof(int32_t))
999
2.23k
        {
1000
2.23k
            memcpy (ptr, counts, cinfo.width * sizeof(int32_t));
1001
2.23k
        }
1002
0
        else
1003
0
        {
1004
0
            for ( int x = 0; x < cinfo.width; ++x )
1005
0
            {
1006
0
                *((int32_t *)ptr) = counts[x];
1007
0
                ptr += xS;
1008
0
            }
1009
0
        }
1010
2.23k
    }
1011
2.23k
}
1012
1013
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT