/src/openexr/src/lib/OpenEXR/ImfScanLineInputFile.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 ScanLineInputFile |
9 | | // |
10 | | //----------------------------------------------------------------------------- |
11 | | |
12 | | #include "ImfScanLineInputFile.h" |
13 | | |
14 | | #include "Iex.h" |
15 | | |
16 | | #include "IlmThreadPool.h" |
17 | | #if ILMTHREAD_THREADING_ENABLED |
18 | | # include "IlmThreadProcessGroup.h" |
19 | | # include <mutex> |
20 | | #endif |
21 | | |
22 | | #include "ImfFrameBuffer.h" |
23 | | #include "ImfInputPartData.h" |
24 | | |
25 | | #include <vector> |
26 | | |
27 | | OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER |
28 | | |
29 | | namespace { |
30 | | |
31 | | struct ScanLineProcess |
32 | | { |
33 | | ~ScanLineProcess () |
34 | 17.5k | { |
35 | 17.5k | if (!first) |
36 | 2.25k | exr_decoding_destroy (decoder.context, &decoder); |
37 | 17.5k | } |
38 | | |
39 | | void run_decode ( |
40 | | exr_const_context_t ctxt, |
41 | | int pn, |
42 | | const FrameBuffer *outfb, |
43 | | int fbY, |
44 | | int fbLastY, |
45 | | const std::vector<Slice> &filllist); |
46 | | |
47 | | void run_unpack ( |
48 | | exr_const_context_t ctxt, |
49 | | int pn, |
50 | | const FrameBuffer *outfb, |
51 | | int fbY, |
52 | | int fbLastY, |
53 | | const std::vector<Slice> &filllist); |
54 | | |
55 | | void update_pointers ( |
56 | | const FrameBuffer *outfb, |
57 | | int fbY, |
58 | | int fbLastY); |
59 | | |
60 | | void run_fill ( |
61 | | const FrameBuffer *outfb, |
62 | | int fbY, |
63 | | const std::vector<Slice> &filllist); |
64 | | |
65 | | exr_result_t last_decode_err = EXR_ERR_UNKNOWN; |
66 | | bool first = true; |
67 | | exr_chunk_info_t cinfo; |
68 | | exr_decode_pipeline_t decoder = EXR_DECODE_PIPELINE_INITIALIZER; |
69 | | |
70 | | // requirement to use process group |
71 | | ScanLineProcess* next; |
72 | | }; |
73 | | |
74 | | #if ILMTHREAD_THREADING_ENABLED |
75 | | using ScanLineProcessGroup = ILMTHREAD_NAMESPACE::ProcessGroup<ScanLineProcess>; |
76 | | #endif |
77 | | |
78 | | } // empty namespace |
79 | | |
80 | | struct ScanLineInputFile::Data |
81 | | { |
82 | | Data (Context *ctxt, int pN, int nT) |
83 | 2.69k | : _ctxt (ctxt) |
84 | 2.69k | , partNumber (pN) |
85 | 2.69k | , numThreads (nT) |
86 | 2.69k | {} |
87 | | |
88 | | void initialize () |
89 | 2.69k | { |
90 | 2.69k | if (_ctxt->storage (partNumber) != EXR_STORAGE_SCANLINE) |
91 | 0 | throw IEX_NAMESPACE::ArgExc ("File part is not a scanline part"); |
92 | 2.69k | } |
93 | | |
94 | | Context* _ctxt; |
95 | | int partNumber; |
96 | | int numThreads; |
97 | | Header header; |
98 | | bool header_filled = false; |
99 | | |
100 | | // TODO: remove once we can remove deprecated API |
101 | | std::vector<char> _pixel_data_scratch; |
102 | | |
103 | | void readPixels (const FrameBuffer &fb, int scanLine1, int scanLine2); |
104 | | |
105 | | // only keep a single stash of a scanline for things which |
106 | | // are reading one-scanline at a time. if we try to keep a |
107 | | // multi-threaded stash of scanlines, memory grows too rapidly |
108 | | std::unique_ptr<ScanLineProcess> singleScan; |
109 | | std::unique_ptr<ScanLineProcess> checkoutScan () |
110 | 43.4k | { |
111 | 43.4k | #if ILMTHREAD_THREADING_ENABLED |
112 | 43.4k | std::lock_guard<std::mutex> lock (_mx); |
113 | 43.4k | #endif |
114 | 43.4k | if (singleScan) |
115 | 41.7k | return std::move (singleScan); |
116 | 1.71k | return std::make_unique<ScanLineProcess> (); |
117 | 43.4k | } |
118 | | void checkinScan (std::unique_ptr<ScanLineProcess> &sp) |
119 | 42.9k | { |
120 | 42.9k | #if ILMTHREAD_THREADING_ENABLED |
121 | 42.9k | std::lock_guard<std::mutex> lock (_mx); |
122 | 42.9k | #endif |
123 | 42.9k | singleScan = std::move (sp); |
124 | 42.9k | } |
125 | | |
126 | | FrameBuffer frameBuffer; |
127 | | std::vector<Slice> fill_list; |
128 | | |
129 | | #if ILMTHREAD_THREADING_ENABLED |
130 | | std::mutex _mx; |
131 | | |
132 | | class LineBufferTask final : public ILMTHREAD_NAMESPACE::Task |
133 | | { |
134 | | public: |
135 | | LineBufferTask ( |
136 | | ILMTHREAD_NAMESPACE::TaskGroup* group, |
137 | | Data* ifd, |
138 | | ScanLineProcessGroup* lineg, |
139 | | const FrameBuffer* outfb, |
140 | | const exr_chunk_info_t& cinfo, |
141 | | int fby, |
142 | | int endScan) |
143 | 763 | : Task (group) |
144 | 763 | , _outfb (outfb) |
145 | 763 | , _ifd (ifd) |
146 | 763 | , _fby (fby) |
147 | 763 | , _last_fby (endScan) |
148 | 763 | , _line (lineg->pop ()) |
149 | 763 | , _line_group (lineg) |
150 | 763 | { |
151 | 763 | _line->cinfo = cinfo; |
152 | 763 | } |
153 | | |
154 | | ~LineBufferTask () override |
155 | 763 | { |
156 | 763 | _line_group->push (_line); |
157 | 763 | } |
158 | | |
159 | | void execute () override; |
160 | | |
161 | | private: |
162 | | void run_decode (); |
163 | | |
164 | | const FrameBuffer* _outfb; |
165 | | Data* _ifd; |
166 | | int _fby; |
167 | | int _last_fby; |
168 | | ScanLineProcess* _line; |
169 | | ScanLineProcessGroup* _line_group; |
170 | | }; |
171 | | #endif |
172 | | }; |
173 | | |
174 | | ScanLineInputFile::ScanLineInputFile (InputPartData* part) |
175 | 2.69k | : _ctxt (part->context), |
176 | 2.69k | _data (std::make_shared<Data> (&_ctxt, part->partNumber, part->numThreads)) |
177 | 2.69k | { |
178 | 2.69k | _data->initialize (); |
179 | 2.69k | } |
180 | | |
181 | | ScanLineInputFile::ScanLineInputFile ( |
182 | | const char* filename, |
183 | | const ContextInitializer& ctxtinit, |
184 | | int numThreads) |
185 | 0 | : _ctxt (filename, ctxtinit, Context::read_mode_t{}) |
186 | 0 | , _data (std::make_shared<Data> (&_ctxt, 0, numThreads)) |
187 | 0 | { |
188 | 0 | _data->initialize (); |
189 | 0 | } |
190 | | |
191 | | ScanLineInputFile::ScanLineInputFile ( |
192 | | OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is, |
193 | | int numThreads) |
194 | 0 | : ScanLineInputFile ( |
195 | 0 | is.fileName (), |
196 | 0 | ContextInitializer () |
197 | 0 | .silentHeaderParse (true) |
198 | 0 | .strictHeaderValidation (false) |
199 | 0 | .setInputStream (&is), |
200 | 0 | numThreads) |
201 | 0 | { |
202 | 0 | } |
203 | | |
204 | | ScanLineInputFile::ScanLineInputFile (const char filename[], int numThreads) |
205 | 0 | : ScanLineInputFile ( |
206 | 0 | filename, |
207 | 0 | ContextInitializer () |
208 | 0 | .silentHeaderParse (true) |
209 | 0 | .strictHeaderValidation (false), |
210 | 0 | numThreads) |
211 | 0 | { |
212 | 0 | } |
213 | | |
214 | | const char* |
215 | | ScanLineInputFile::fileName () const |
216 | 7 | { |
217 | 7 | return _ctxt.fileName (); |
218 | 7 | } |
219 | | |
220 | | const Header& |
221 | | ScanLineInputFile::header () const |
222 | 0 | { |
223 | 0 | #if ILMTHREAD_THREADING_ENABLED |
224 | 0 | std::lock_guard<std::mutex> lock (_data->_mx); |
225 | 0 | #endif |
226 | 0 | if (!_data->header_filled) |
227 | 0 | { |
228 | 0 | _data->header = _ctxt.header (_data->partNumber); |
229 | 0 | _data->header_filled = true; |
230 | 0 | } |
231 | 0 | return _data->header; |
232 | 0 | } |
233 | | |
234 | | int |
235 | | ScanLineInputFile::version () const |
236 | 0 | { |
237 | 0 | return _ctxt.version (); |
238 | 0 | } |
239 | | |
240 | | void |
241 | | ScanLineInputFile::setFrameBuffer (const FrameBuffer& frameBuffer) |
242 | 3.23k | { |
243 | 3.23k | #if ILMTHREAD_THREADING_ENABLED |
244 | 3.23k | std::lock_guard<std::mutex> lock (_data->_mx); |
245 | 3.23k | #endif |
246 | 3.23k | _data->fill_list.clear (); |
247 | 3.23k | _data->singleScan.reset(); |
248 | | |
249 | 3.23k | for (FrameBuffer::ConstIterator j = frameBuffer.begin (); |
250 | 12.8k | j != frameBuffer.end (); |
251 | 9.58k | ++j) |
252 | 9.59k | { |
253 | 9.59k | const exr_attr_chlist_entry_t* curc = _ctxt.findChannel ( |
254 | 9.59k | _data->partNumber, j.name ()); |
255 | | |
256 | 9.59k | if (!curc) |
257 | 6.97k | { |
258 | 6.97k | _data->fill_list.push_back (j.slice ()); |
259 | 6.97k | continue; |
260 | 6.97k | } |
261 | | |
262 | 2.61k | if (curc->x_sampling != j.slice ().xSampling || |
263 | 2.61k | curc->y_sampling != j.slice ().ySampling) |
264 | 7 | THROW ( |
265 | 2.61k | IEX_NAMESPACE::ArgExc, |
266 | 2.61k | "X and/or y subsampling factors " |
267 | 2.61k | "of \"" |
268 | 2.61k | << j.name () |
269 | 2.61k | << "\" channel " |
270 | 2.61k | "of input file \"" |
271 | 2.61k | << fileName () |
272 | 2.61k | << "\" are " |
273 | 2.61k | "not compatible with the frame buffer's " |
274 | 2.61k | "subsampling factors."); |
275 | 2.61k | } |
276 | | |
277 | 3.23k | _data->frameBuffer = frameBuffer; |
278 | 3.23k | } |
279 | | |
280 | | const FrameBuffer& |
281 | | ScanLineInputFile::frameBuffer () const |
282 | 44.4k | { |
283 | 44.4k | #if ILMTHREAD_THREADING_ENABLED |
284 | 44.4k | std::lock_guard<std::mutex> lock (_data->_mx); |
285 | 44.4k | #endif |
286 | 44.4k | return _data->frameBuffer; |
287 | 44.4k | } |
288 | | |
289 | | bool |
290 | | ScanLineInputFile::isComplete () const |
291 | 0 | { |
292 | 0 | return _ctxt.chunkTableValid (_data->partNumber); |
293 | 0 | } |
294 | | |
295 | | bool |
296 | | ScanLineInputFile::isOptimizationEnabled () const |
297 | 0 | { |
298 | | // TODO: the core library has a number of special cased patterns, |
299 | | // this is all kind of ... not useful? for now, return a pattern |
300 | | // similar to legacy version |
301 | 0 | return _ctxt.channels (_data->partNumber)->num_channels != 2; |
302 | 0 | } |
303 | | |
304 | | void |
305 | | ScanLineInputFile::readPixels (int scanLine1, int scanLine2) |
306 | 44.4k | { |
307 | 44.4k | _data->readPixels (frameBuffer (), scanLine1, scanLine2); |
308 | 44.4k | } |
309 | | |
310 | | void |
311 | | ScanLineInputFile::readPixels ( |
312 | | const FrameBuffer& frame, int scanLine1, int scanLine2) |
313 | 0 | { |
314 | 0 | _data->readPixels (frame, scanLine1, scanLine2); |
315 | 0 | } |
316 | | |
317 | | //////////////////////////////////////// |
318 | | |
319 | | void |
320 | | ScanLineInputFile::readPixels (int scanLine) |
321 | 0 | { |
322 | 0 | readPixels (scanLine, scanLine); |
323 | 0 | } |
324 | | |
325 | | //////////////////////////////////////// |
326 | | |
327 | | void |
328 | | ScanLineInputFile::rawPixelData ( |
329 | | int firstScanLine, const char*& pixelData, int& pixelDataSize) |
330 | 0 | { |
331 | 0 | uint64_t maxsize = 0; |
332 | 0 | if (EXR_ERR_SUCCESS != |
333 | 0 | exr_get_chunk_unpacked_size (_ctxt, _data->partNumber, &maxsize)) |
334 | 0 | { |
335 | 0 | THROW ( |
336 | 0 | IEX_NAMESPACE::ArgExc, |
337 | 0 | "Unable to query data size of chunk in file '" << fileName () |
338 | 0 | << "'"); |
339 | 0 | } |
340 | | |
341 | | // again, doesn't actually provide any safety given we're handing |
342 | | // back a pointer... but will at least prevent two threads |
343 | | // allocating at the same time and getting sliced |
344 | 0 | #if ILMTHREAD_THREADING_ENABLED |
345 | 0 | std::lock_guard<std::mutex> lock (_data->_mx); |
346 | 0 | #endif |
347 | 0 | _data->_pixel_data_scratch.resize (maxsize); |
348 | |
|
349 | 0 | pixelData = _data->_pixel_data_scratch.data (); |
350 | 0 | pixelDataSize = static_cast<int> (maxsize); |
351 | |
|
352 | 0 | rawPixelDataToBuffer ( |
353 | 0 | firstScanLine, _data->_pixel_data_scratch.data (), pixelDataSize); |
354 | 0 | } |
355 | | |
356 | | void |
357 | | ScanLineInputFile::rawPixelDataToBuffer ( |
358 | | int scanLine, char* pixelData, int& pixelDataSize) const |
359 | 0 | { |
360 | 0 | exr_chunk_info_t cinfo; |
361 | 0 | if (EXR_ERR_SUCCESS == exr_read_scanline_chunk_info ( |
362 | 0 | _ctxt, _data->partNumber, scanLine, &cinfo)) |
363 | 0 | { |
364 | 0 | if (pixelDataSize < 0 || |
365 | 0 | cinfo.packed_size > static_cast<uint64_t> (pixelDataSize)) |
366 | 0 | { |
367 | 0 | THROW ( |
368 | 0 | IEX_NAMESPACE::ArgExc, |
369 | 0 | "Error reading pixel data from image " |
370 | 0 | "file \"" |
371 | 0 | << fileName () |
372 | 0 | << "\". Provided buffer is too small to read raw pixel data:" |
373 | 0 | << pixelDataSize << " bytes."); |
374 | 0 | } |
375 | | |
376 | 0 | pixelDataSize = static_cast<int> (cinfo.packed_size); |
377 | |
|
378 | 0 | if (EXR_ERR_SUCCESS != |
379 | 0 | exr_read_chunk (_ctxt, _data->partNumber, &cinfo, pixelData)) |
380 | 0 | { |
381 | 0 | THROW ( |
382 | 0 | IEX_NAMESPACE::ArgExc, |
383 | 0 | "Error reading pixel data from image " |
384 | 0 | "file \"" |
385 | 0 | << fileName () << "\". Unable to read raw pixel data of " |
386 | 0 | << pixelDataSize << " bytes."); |
387 | 0 | } |
388 | 0 | } |
389 | 0 | else |
390 | 0 | { |
391 | 0 | if (_ctxt.storage (_data->partNumber) == EXR_STORAGE_TILED) |
392 | 0 | { |
393 | 0 | THROW ( |
394 | 0 | IEX_NAMESPACE::ArgExc, |
395 | 0 | "Error reading pixel data from image " |
396 | 0 | "file \"" |
397 | 0 | << fileName () |
398 | 0 | << "\". Tried to read a raw scanline from a tiled image."); |
399 | 0 | } |
400 | 0 | else |
401 | 0 | { |
402 | 0 | THROW ( |
403 | 0 | IEX_NAMESPACE::ArgExc, |
404 | 0 | "Error reading pixel data from image " |
405 | 0 | "file \"" |
406 | 0 | << fileName () |
407 | 0 | << "\". Unable to query data block information."); |
408 | 0 | } |
409 | 0 | } |
410 | 0 | } |
411 | | |
412 | | //////////////////////////////////////// |
413 | | |
414 | | void ScanLineInputFile::Data::readPixels ( |
415 | | const FrameBuffer &fb, int scanLine1, int scanLine2) |
416 | 44.4k | { |
417 | 44.4k | exr_attr_box2i_t dw = _ctxt->dataWindow (partNumber); |
418 | 44.4k | exr_chunk_info_t cinfo; |
419 | 44.4k | int32_t scansperchunk = 1; |
420 | | |
421 | 44.4k | if (EXR_ERR_SUCCESS != exr_get_scanlines_per_chunk (*_ctxt, partNumber, &scansperchunk)) |
422 | 0 | { |
423 | 0 | THROW ( |
424 | 0 | IEX_NAMESPACE::ArgExc, |
425 | 0 | "Error querying scanline counts from image " |
426 | 0 | "file \"" << _ctxt->fileName () << "\"."); |
427 | 0 | } |
428 | | |
429 | 44.4k | if (scanLine2 < scanLine1) |
430 | 0 | std::swap (scanLine1, scanLine2); |
431 | | |
432 | 44.4k | if (scanLine1 < dw.min.y || scanLine2 > dw.max.y) |
433 | 9 | { |
434 | 9 | THROW ( |
435 | 9 | IEX_NAMESPACE::ArgExc, |
436 | 9 | "Tried to read scan line outside " |
437 | 9 | "the image file's data window: " |
438 | 9 | << scanLine1 << " - " << scanLine2 |
439 | 9 | << " vs datawindow " |
440 | 9 | << dw.min.y << " - " << dw.max.y); |
441 | 9 | } |
442 | | |
443 | 44.4k | #if ILMTHREAD_THREADING_ENABLED |
444 | 44.4k | int64_t nchunks; |
445 | 44.4k | nchunks = ((int64_t) scanLine2 - (int64_t) scanLine1); |
446 | 44.4k | nchunks /= (int64_t) scansperchunk; |
447 | 44.4k | nchunks += 1; |
448 | | |
449 | 44.4k | if (nchunks > 1 && numThreads > 1) |
450 | 989 | { |
451 | | // we need the lifetime of this to last longer than the |
452 | | // lifetime of the task group below such that we don't get use |
453 | | // after free type error, so use scope rules to accomplish |
454 | | // this |
455 | 989 | ScanLineProcessGroup sg (numThreads); |
456 | | |
457 | 989 | { |
458 | 989 | ILMTHREAD_NAMESPACE::TaskGroup tg; |
459 | | |
460 | 1.75k | for (int y = scanLine1; y <= scanLine2; ) |
461 | 1.59k | { |
462 | 1.59k | if (EXR_ERR_SUCCESS != exr_read_scanline_chunk_info (*_ctxt, partNumber, y, &cinfo)) |
463 | 831 | throw IEX_NAMESPACE::InputExc ("Unable to query scanline information"); |
464 | | |
465 | 763 | ILMTHREAD_NAMESPACE::ThreadPool::addGlobalTask ( |
466 | 763 | new LineBufferTask (&tg, this, &sg, &fb, cinfo, y, scanLine2) ); |
467 | | |
468 | 763 | y += scansperchunk - (y - cinfo.start_y); |
469 | 763 | } |
470 | 989 | } |
471 | | |
472 | 158 | sg.throw_on_failure (); |
473 | 158 | } |
474 | 43.4k | else |
475 | 43.4k | #endif |
476 | 43.4k | { |
477 | 43.4k | std::unique_ptr<ScanLineProcess> sp = checkoutScan (); |
478 | | |
479 | 86.8k | for (int y = scanLine1; y <= scanLine2; ) |
480 | 43.4k | { |
481 | 43.4k | if (EXR_ERR_SUCCESS != exr_read_scanline_chunk_info (*_ctxt, partNumber, y, &cinfo)) |
482 | 145 | throw IEX_NAMESPACE::InputExc ("Unable to query scanline information"); |
483 | | |
484 | | // check if we have the same chunk where we can just |
485 | | // re-run the unpack (i.e. people reading 1 scan at a time |
486 | | // in a multi-scanline chunk) |
487 | 43.3k | if (!sp->first && sp->cinfo.idx == cinfo.idx && |
488 | 41.7k | sp->last_decode_err == EXR_ERR_SUCCESS) |
489 | 41.7k | { |
490 | 41.7k | sp->run_unpack ( |
491 | 41.7k | *_ctxt, |
492 | 41.7k | partNumber, |
493 | 41.7k | &fb, |
494 | 41.7k | y, |
495 | 41.7k | scanLine2, |
496 | 41.7k | fill_list); |
497 | 41.7k | } |
498 | 1.60k | else |
499 | 1.60k | { |
500 | 1.60k | sp->cinfo = cinfo; |
501 | 1.60k | sp->run_decode ( |
502 | 1.60k | *_ctxt, |
503 | 1.60k | partNumber, |
504 | 1.60k | &fb, |
505 | 1.60k | y, |
506 | 1.60k | scanLine2, |
507 | 1.60k | fill_list); |
508 | 1.60k | } |
509 | | |
510 | 43.3k | y += scansperchunk - (y - cinfo.start_y); |
511 | 43.3k | } |
512 | | |
513 | 43.3k | checkinScan (sp); |
514 | 43.3k | } |
515 | 44.4k | } |
516 | | |
517 | | //////////////////////////////////////// |
518 | | |
519 | | #if ILMTHREAD_THREADING_ENABLED |
520 | | void ScanLineInputFile::Data::LineBufferTask::execute () |
521 | 763 | { |
522 | 763 | try |
523 | 763 | { |
524 | 763 | _line->run_decode ( |
525 | 763 | *(_ifd->_ctxt), |
526 | 763 | _ifd->partNumber, |
527 | 763 | _outfb, |
528 | 763 | _fby, |
529 | 763 | _last_fby, |
530 | 763 | _ifd->fill_list); |
531 | 763 | } |
532 | 763 | catch (std::exception &e) |
533 | 763 | { |
534 | 295 | _line_group->record_failure (e.what ()); |
535 | 295 | } |
536 | 763 | catch (...) |
537 | 763 | { |
538 | 0 | _line_group->record_failure ("Unknown exception"); |
539 | 0 | } |
540 | 763 | } |
541 | | #endif |
542 | | |
543 | | //////////////////////////////////////// |
544 | | |
545 | | void ScanLineProcess::run_decode ( |
546 | | exr_const_context_t ctxt, |
547 | | int pn, |
548 | | const FrameBuffer *outfb, |
549 | | int fbY, |
550 | | int fbLastY, |
551 | | const std::vector<Slice> &filllist) |
552 | 2.36k | { |
553 | 2.36k | last_decode_err = EXR_ERR_UNKNOWN; |
554 | | // stash the flag off to make sure to clean up in the event |
555 | | // of an exception by changing the flag after init... |
556 | 2.36k | bool isfirst = first; |
557 | 2.36k | if (first) |
558 | 2.25k | { |
559 | 2.25k | if (EXR_ERR_SUCCESS != |
560 | 2.25k | exr_decoding_initialize (ctxt, pn, &cinfo, &decoder)) |
561 | 0 | { |
562 | 0 | throw IEX_NAMESPACE::IoExc ("Unable to initialize decode pipeline"); |
563 | 0 | } |
564 | | |
565 | 2.25k | first = false; |
566 | 2.25k | } |
567 | 119 | else |
568 | 119 | { |
569 | 119 | if (EXR_ERR_SUCCESS != |
570 | 119 | exr_decoding_update (ctxt, pn, &cinfo, &decoder)) |
571 | 0 | { |
572 | 0 | throw IEX_NAMESPACE::IoExc ("Unable to update decode pipeline"); |
573 | 0 | } |
574 | 119 | } |
575 | | |
576 | 2.36k | update_pointers (outfb, fbY, fbLastY); |
577 | | |
578 | 2.36k | if (isfirst) |
579 | 2.24k | { |
580 | 2.24k | if (EXR_ERR_SUCCESS != |
581 | 2.24k | exr_decoding_choose_default_routines (ctxt, pn, &decoder)) |
582 | 0 | { |
583 | 0 | throw IEX_NAMESPACE::IoExc ("Unable to choose decoder routines"); |
584 | 0 | } |
585 | 2.24k | } |
586 | | |
587 | 2.36k | last_decode_err = exr_decoding_run (ctxt, pn, &decoder); |
588 | 2.36k | if (EXR_ERR_SUCCESS != last_decode_err) |
589 | 723 | throw IEX_NAMESPACE::IoExc ("Unable to run decoder"); |
590 | | |
591 | 1.64k | run_fill (outfb, fbY, filllist); |
592 | 1.64k | } |
593 | | |
594 | | //////////////////////////////////////// |
595 | | |
596 | | void ScanLineProcess::run_unpack ( |
597 | | exr_const_context_t ctxt, |
598 | | int pn, |
599 | | const FrameBuffer *outfb, |
600 | | int fbY, |
601 | | int fbLastY, |
602 | | const std::vector<Slice> &filllist) |
603 | 41.7k | { |
604 | 41.7k | update_pointers (outfb, fbY, fbLastY); |
605 | | |
606 | | /* won't work for deep where we need to re-allocate the number of |
607 | | * samples but for normal scanlines is fine to just bypass pipe |
608 | | * and run the unpacker */ |
609 | 41.7k | if (decoder.chunk.unpacked_size > 0 && decoder.unpack_and_convert_fn) |
610 | 41.7k | { |
611 | 41.7k | last_decode_err = decoder.unpack_and_convert_fn (&decoder); |
612 | 41.7k | if (EXR_ERR_SUCCESS != last_decode_err) |
613 | 0 | throw IEX_NAMESPACE::IoExc ("Unable to run decoder"); |
614 | 41.7k | } |
615 | | |
616 | 41.7k | run_fill (outfb, fbY, filllist); |
617 | 41.7k | } |
618 | | |
619 | | //////////////////////////////////////// |
620 | | |
621 | | void ScanLineProcess::update_pointers ( |
622 | | const FrameBuffer *outfb, int fbY, int fbLastY) |
623 | 44.1k | { |
624 | 44.1k | decoder.user_line_begin_skip = fbY - cinfo.start_y; |
625 | 44.1k | decoder.user_line_end_ignore = 0; |
626 | 44.1k | int64_t endY = (int64_t)cinfo.start_y + (int64_t)cinfo.height - 1; |
627 | 44.1k | if ((int64_t)fbLastY < endY) |
628 | 41.6k | decoder.user_line_end_ignore = (int32_t)(endY - fbLastY); |
629 | | |
630 | 102k | for (int c = 0; c < decoder.channel_count; ++c) |
631 | 58.1k | { |
632 | 58.1k | exr_coding_channel_info_t& curchan = decoder.channels[c]; |
633 | 58.1k | uint8_t* ptr; |
634 | 58.1k | const Slice* fbslice; |
635 | | |
636 | 58.1k | fbslice = outfb->findSlice (curchan.channel_name); |
637 | | |
638 | 58.1k | if (curchan.height == 0 || !fbslice) |
639 | 14.3k | { |
640 | 14.3k | curchan.decode_to_ptr = NULL; |
641 | 14.3k | curchan.user_pixel_stride = 0; |
642 | 14.3k | curchan.user_line_stride = 0; |
643 | 14.3k | continue; |
644 | 14.3k | } |
645 | | |
646 | 43.8k | curchan.user_bytes_per_element = (fbslice->type == HALF) ? 2 : 4; |
647 | 43.8k | curchan.user_data_type = (exr_pixel_type_t)fbslice->type; |
648 | 43.8k | curchan.user_pixel_stride = fbslice->xStride; |
649 | 43.8k | curchan.user_line_stride = fbslice->yStride; |
650 | | |
651 | 43.8k | ptr = reinterpret_cast<uint8_t*> (fbslice->base); |
652 | 43.8k | ptr += int64_t (cinfo.start_x / fbslice->xSampling) * int64_t (fbslice->xStride); |
653 | 43.8k | ptr += int64_t (fbY / fbslice->ySampling) * int64_t (fbslice->yStride); |
654 | | |
655 | 43.8k | curchan.decode_to_ptr = ptr; |
656 | 43.8k | } |
657 | 44.1k | } |
658 | | |
659 | | //////////////////////////////////////// |
660 | | |
661 | | void ScanLineProcess::run_fill ( |
662 | | const FrameBuffer *outfb, |
663 | | int fbY, |
664 | | const std::vector<Slice> &filllist) |
665 | 43.3k | { |
666 | 43.3k | for (auto& s: filllist) |
667 | 128k | { |
668 | 128k | uint8_t* ptr; |
669 | | |
670 | 128k | ptr = reinterpret_cast<uint8_t*> (s.base); |
671 | 128k | ptr += int64_t (cinfo.start_x / s.xSampling) * int64_t (s.xStride); |
672 | 128k | ptr += int64_t (fbY / s.ySampling) * int64_t (s.yStride); |
673 | | |
674 | | // TODO: update ImfMisc, lift fill type / value |
675 | 128k | int stop = cinfo.start_y + cinfo.height - decoder.user_line_end_ignore; |
676 | 324k | for ( int start = fbY; start < stop; ++start ) |
677 | 154k | { |
678 | 154k | if (start % s.ySampling) continue; |
679 | | |
680 | 132k | uint8_t* outptr = ptr; |
681 | 132k | for ( int sx = cinfo.start_x, ex = cinfo.start_x + cinfo.width; |
682 | 590M | sx < ex; ++sx ) |
683 | 590M | { |
684 | 590M | if (sx % s.xSampling) continue; |
685 | | |
686 | 461M | switch (s.type) |
687 | 461M | { |
688 | 0 | case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT: |
689 | 0 | { |
690 | 0 | unsigned int fillVal = (unsigned int) (s.fillValue); |
691 | 0 | *(unsigned int*)outptr = fillVal; |
692 | 0 | break; |
693 | 0 | } |
694 | | |
695 | 461M | case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF: |
696 | 461M | { |
697 | 461M | half fillVal = half (s.fillValue); |
698 | 461M | *(half*)outptr = fillVal; |
699 | 461M | break; |
700 | 0 | } |
701 | | |
702 | 0 | case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT: |
703 | 0 | { |
704 | 0 | float fillVal = float (s.fillValue); |
705 | 0 | *(float*)outptr = fillVal; |
706 | 0 | break; |
707 | 0 | } |
708 | 0 | default: |
709 | 0 | throw IEX_NAMESPACE::ArgExc ("Unknown pixel data type."); |
710 | 461M | } |
711 | 461M | outptr += s.xStride; |
712 | 461M | } |
713 | | |
714 | 173k | ptr += s.yStride; |
715 | 173k | } |
716 | 128k | } |
717 | 43.3k | } |
718 | | |
719 | | OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT |