/src/freeimage-svn/FreeImage/trunk/Source/OpenEXR/IlmImf/ImfRgbaFile.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /////////////////////////////////////////////////////////////////////////// |
2 | | // |
3 | | // Copyright (c) 2004, Industrial Light & Magic, a division of Lucas |
4 | | // Digital Ltd. LLC |
5 | | // |
6 | | // All rights reserved. |
7 | | // |
8 | | // Redistribution and use in source and binary forms, with or without |
9 | | // modification, are permitted provided that the following conditions are |
10 | | // met: |
11 | | // * Redistributions of source code must retain the above copyright |
12 | | // notice, this list of conditions and the following disclaimer. |
13 | | // * Redistributions in binary form must reproduce the above |
14 | | // copyright notice, this list of conditions and the following disclaimer |
15 | | // in the documentation and/or other materials provided with the |
16 | | // distribution. |
17 | | // * Neither the name of Industrial Light & Magic nor the names of |
18 | | // its contributors may be used to endorse or promote products derived |
19 | | // from this software without specific prior written permission. |
20 | | // |
21 | | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
22 | | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
23 | | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
24 | | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
25 | | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
26 | | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
27 | | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
28 | | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
29 | | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
30 | | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
31 | | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
32 | | // |
33 | | /////////////////////////////////////////////////////////////////////////// |
34 | | |
35 | | //----------------------------------------------------------------------------- |
36 | | // |
37 | | // class RgbaOutputFile |
38 | | // class RgbaInputFile |
39 | | // |
40 | | //----------------------------------------------------------------------------- |
41 | | |
42 | | #include <ImfRgbaFile.h> |
43 | | #include <ImfOutputFile.h> |
44 | | #include <ImfInputFile.h> |
45 | | #include <ImfChannelList.h> |
46 | | #include <ImfRgbaYca.h> |
47 | | #include <ImfStandardAttributes.h> |
48 | | #include <ImathFun.h> |
49 | | #include <IlmThreadMutex.h> |
50 | | #include <Iex.h> |
51 | | #include <string.h> |
52 | | #include <algorithm> |
53 | | |
54 | | #include "ImfNamespace.h" |
55 | | |
56 | | OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER |
57 | | |
58 | | using namespace std; |
59 | | using namespace IMATH_NAMESPACE; |
60 | | using namespace RgbaYca; |
61 | | using namespace ILMTHREAD_NAMESPACE; |
62 | | |
63 | | namespace { |
64 | | |
65 | | void |
66 | | insertChannels (Header &header, RgbaChannels rgbaChannels) |
67 | 0 | { |
68 | 0 | ChannelList ch; |
69 | |
|
70 | 0 | if (rgbaChannels & (WRITE_Y | WRITE_C)) |
71 | 0 | { |
72 | 0 | if (rgbaChannels & WRITE_Y) |
73 | 0 | { |
74 | 0 | ch.insert ("Y", Channel (HALF, 1, 1)); |
75 | 0 | } |
76 | |
|
77 | 0 | if (rgbaChannels & WRITE_C) |
78 | 0 | { |
79 | 0 | ch.insert ("RY", Channel (HALF, 2, 2, true)); |
80 | 0 | ch.insert ("BY", Channel (HALF, 2, 2, true)); |
81 | 0 | } |
82 | 0 | } |
83 | 0 | else |
84 | 0 | { |
85 | 0 | if (rgbaChannels & WRITE_R) |
86 | 0 | ch.insert ("R", Channel (HALF, 1, 1)); |
87 | |
|
88 | 0 | if (rgbaChannels & WRITE_G) |
89 | 0 | ch.insert ("G", Channel (HALF, 1, 1)); |
90 | |
|
91 | 0 | if (rgbaChannels & WRITE_B) |
92 | 0 | ch.insert ("B", Channel (HALF, 1, 1)); |
93 | 0 | } |
94 | |
|
95 | 0 | if (rgbaChannels & WRITE_A) |
96 | 0 | ch.insert ("A", Channel (HALF, 1, 1)); |
97 | |
|
98 | 0 | header.channels() = ch; |
99 | 0 | } |
100 | | |
101 | | |
102 | | RgbaChannels |
103 | | rgbaChannels (const ChannelList &ch, const string &channelNamePrefix = "") |
104 | 0 | { |
105 | 0 | int i = 0; |
106 | |
|
107 | 0 | if (ch.findChannel (channelNamePrefix + "R")) |
108 | 0 | i |= WRITE_R; |
109 | |
|
110 | 0 | if (ch.findChannel (channelNamePrefix + "G")) |
111 | 0 | i |= WRITE_G; |
112 | | |
113 | 0 | if (ch.findChannel (channelNamePrefix + "B")) |
114 | 0 | i |= WRITE_B; |
115 | |
|
116 | 0 | if (ch.findChannel (channelNamePrefix + "A")) |
117 | 0 | i |= WRITE_A; |
118 | |
|
119 | 0 | if (ch.findChannel (channelNamePrefix + "Y")) |
120 | 0 | i |= WRITE_Y; |
121 | |
|
122 | 0 | if (ch.findChannel (channelNamePrefix + "RY") || |
123 | 0 | ch.findChannel (channelNamePrefix + "BY")) |
124 | 0 | i |= WRITE_C; |
125 | |
|
126 | 0 | return RgbaChannels (i); |
127 | 0 | } |
128 | | |
129 | | |
130 | | string |
131 | | prefixFromLayerName (const string &layerName, const Header &header) |
132 | 0 | { |
133 | 0 | if (layerName.empty()) |
134 | 0 | return ""; |
135 | | |
136 | 0 | if (hasMultiView (header) && multiView(header)[0] == layerName) |
137 | 0 | return ""; |
138 | | |
139 | 0 | return layerName + "."; |
140 | 0 | } |
141 | | |
142 | | |
143 | | V3f |
144 | | ywFromHeader (const Header &header) |
145 | 0 | { |
146 | 0 | Chromaticities cr; |
147 | |
|
148 | 0 | if (hasChromaticities (header)) |
149 | 0 | cr = chromaticities (header); |
150 | |
|
151 | 0 | return computeYw (cr); |
152 | 0 | } |
153 | | |
154 | | |
155 | | ptrdiff_t |
156 | | cachePadding (ptrdiff_t size) |
157 | 0 | { |
158 | | // |
159 | | // Some of the buffers that are allocated by classes ToYca and |
160 | | // FromYca, below, may need to be padded to avoid cache thrashing. |
161 | | // If the difference between the buffer size and the nearest power |
162 | | // of two is less than CACHE_LINE_SIZE, then we add an appropriate |
163 | | // amount of padding. |
164 | | // |
165 | | // CACHE_LINE_SIZE must be a power of two, and it must be at |
166 | | // least as big as the true size of a cache line on the machine |
167 | | // we are running on. (It is ok if CACHE_LINE_SIZE is larger |
168 | | // than a real cache line.) |
169 | | // |
170 | |
|
171 | 0 | static int LOG2_CACHE_LINE_SIZE = 8; |
172 | 0 | static const ptrdiff_t CACHE_LINE_SIZE = (1 << LOG2_CACHE_LINE_SIZE); |
173 | |
|
174 | 0 | int i = LOG2_CACHE_LINE_SIZE + 2; |
175 | |
|
176 | 0 | while ((size >> i) > 1) |
177 | 0 | ++i; |
178 | |
|
179 | 0 | if (size > (1 << (i + 1)) - 64) |
180 | 0 | return 64 + ((1 << (i + 1)) - size); |
181 | | |
182 | 0 | if (size < (1 << i) + 64) |
183 | 0 | return 64 + ((1 << i) - size); |
184 | | |
185 | 0 | return 0; |
186 | 0 | } |
187 | | |
188 | | } // namespace |
189 | | |
190 | | |
191 | | class RgbaOutputFile::ToYca: public Mutex |
192 | | { |
193 | | public: |
194 | | |
195 | | ToYca (OutputFile &outputFile, RgbaChannels rgbaChannels); |
196 | | ~ToYca (); |
197 | | |
198 | | void setYCRounding (unsigned int roundY, |
199 | | unsigned int roundC); |
200 | | |
201 | | void setFrameBuffer (const Rgba *base, |
202 | | size_t xStride, |
203 | | size_t yStride); |
204 | | |
205 | | void writePixels (int numScanLines); |
206 | | int currentScanLine () const; |
207 | | |
208 | | private: |
209 | | |
210 | | void padTmpBuf (); |
211 | | void rotateBuffers (); |
212 | | void duplicateLastBuffer (); |
213 | | void duplicateSecondToLastBuffer (); |
214 | | void decimateChromaVertAndWriteScanLine (); |
215 | | |
216 | | OutputFile & _outputFile; |
217 | | bool _writeY; |
218 | | bool _writeC; |
219 | | bool _writeA; |
220 | | int _xMin; |
221 | | int _width; |
222 | | int _height; |
223 | | int _linesConverted; |
224 | | LineOrder _lineOrder; |
225 | | int _currentScanLine; |
226 | | V3f _yw; |
227 | | Rgba * _bufBase; |
228 | | Rgba * _buf[N]; |
229 | | Rgba * _tmpBuf; |
230 | | const Rgba * _fbBase; |
231 | | size_t _fbXStride; |
232 | | size_t _fbYStride; |
233 | | int _roundY; |
234 | | int _roundC; |
235 | | }; |
236 | | |
237 | | |
238 | | RgbaOutputFile::ToYca::ToYca (OutputFile &outputFile, |
239 | | RgbaChannels rgbaChannels) |
240 | | : |
241 | | _outputFile (outputFile) |
242 | 0 | { |
243 | 0 | _writeY = (rgbaChannels & WRITE_Y)? true: false; |
244 | 0 | _writeC = (rgbaChannels & WRITE_C)? true: false; |
245 | 0 | _writeA = (rgbaChannels & WRITE_A)? true: false; |
246 | |
|
247 | 0 | const Box2i dw = _outputFile.header().dataWindow(); |
248 | |
|
249 | 0 | _xMin = dw.min.x; |
250 | 0 | _width = dw.max.x - dw.min.x + 1; |
251 | 0 | _height = dw.max.y - dw.min.y + 1; |
252 | |
|
253 | 0 | _linesConverted = 0; |
254 | 0 | _lineOrder = _outputFile.header().lineOrder(); |
255 | | |
256 | 0 | if (_lineOrder == INCREASING_Y) |
257 | 0 | _currentScanLine = dw.min.y; |
258 | 0 | else |
259 | 0 | _currentScanLine = dw.max.y; |
260 | |
|
261 | 0 | _yw = ywFromHeader (_outputFile.header()); |
262 | |
|
263 | 0 | ptrdiff_t pad = cachePadding (_width * sizeof (Rgba)) / sizeof (Rgba); |
264 | |
|
265 | 0 | _bufBase = new Rgba[(_width + pad) * N]; |
266 | |
|
267 | 0 | for (int i = 0; i < N; ++i) |
268 | 0 | _buf[i] = _bufBase + (i * (_width + pad)); |
269 | |
|
270 | 0 | _tmpBuf = new Rgba[_width + N - 1]; |
271 | |
|
272 | 0 | _fbBase = 0; |
273 | 0 | _fbXStride = 0; |
274 | 0 | _fbYStride = 0; |
275 | |
|
276 | 0 | _roundY = 7; |
277 | 0 | _roundC = 5; |
278 | 0 | } |
279 | | |
280 | | |
281 | | RgbaOutputFile::ToYca::~ToYca () |
282 | 0 | { |
283 | 0 | delete [] _bufBase; |
284 | 0 | delete [] _tmpBuf; |
285 | 0 | } |
286 | | |
287 | | |
288 | | void |
289 | | RgbaOutputFile::ToYca::setYCRounding (unsigned int roundY, |
290 | | unsigned int roundC) |
291 | 0 | { |
292 | 0 | _roundY = roundY; |
293 | 0 | _roundC = roundC; |
294 | 0 | } |
295 | | |
296 | | |
297 | | void |
298 | | RgbaOutputFile::ToYca::setFrameBuffer (const Rgba *base, |
299 | | size_t xStride, |
300 | | size_t yStride) |
301 | 0 | { |
302 | 0 | if (_fbBase == 0) |
303 | 0 | { |
304 | 0 | FrameBuffer fb; |
305 | |
|
306 | 0 | if (_writeY) |
307 | 0 | { |
308 | 0 | fb.insert ("Y", |
309 | 0 | Slice (HALF, // type |
310 | 0 | (char *) &_tmpBuf[-_xMin].g, // base |
311 | 0 | sizeof (Rgba), // xStride |
312 | 0 | 0, // yStride |
313 | 0 | 1, // xSampling |
314 | 0 | 1)); // ySampling |
315 | 0 | } |
316 | |
|
317 | 0 | if (_writeC) |
318 | 0 | { |
319 | 0 | fb.insert ("RY", |
320 | 0 | Slice (HALF, // type |
321 | 0 | (char *) &_tmpBuf[-_xMin].r, // base |
322 | 0 | sizeof (Rgba) * 2, // xStride |
323 | 0 | 0, // yStride |
324 | 0 | 2, // xSampling |
325 | 0 | 2)); // ySampling |
326 | |
|
327 | 0 | fb.insert ("BY", |
328 | 0 | Slice (HALF, // type |
329 | 0 | (char *) &_tmpBuf[-_xMin].b, // base |
330 | 0 | sizeof (Rgba) * 2, // xStride |
331 | 0 | 0, // yStride |
332 | 0 | 2, // xSampling |
333 | 0 | 2)); // ySampling |
334 | 0 | } |
335 | |
|
336 | 0 | if (_writeA) |
337 | 0 | { |
338 | 0 | fb.insert ("A", |
339 | 0 | Slice (HALF, // type |
340 | 0 | (char *) &_tmpBuf[-_xMin].a, // base |
341 | 0 | sizeof (Rgba), // xStride |
342 | 0 | 0, // yStride |
343 | 0 | 1, // xSampling |
344 | 0 | 1)); // ySampling |
345 | 0 | } |
346 | |
|
347 | 0 | _outputFile.setFrameBuffer (fb); |
348 | 0 | } |
349 | |
|
350 | 0 | _fbBase = base; |
351 | 0 | _fbXStride = xStride; |
352 | 0 | _fbYStride = yStride; |
353 | 0 | } |
354 | | |
355 | | |
356 | | void |
357 | | RgbaOutputFile::ToYca::writePixels (int numScanLines) |
358 | 0 | { |
359 | 0 | if (_fbBase == 0) |
360 | 0 | { |
361 | 0 | THROW (IEX_NAMESPACE::ArgExc, "No frame buffer was specified as the " |
362 | 0 | "pixel data source for image file " |
363 | 0 | "\"" << _outputFile.fileName() << "\"."); |
364 | 0 | } |
365 | | |
366 | 0 | if (_writeY && !_writeC) |
367 | 0 | { |
368 | | // |
369 | | // We are writing only luminance; filtering |
370 | | // and subsampling are not necessary. |
371 | | // |
372 | |
|
373 | 0 | for (int i = 0; i < numScanLines; ++i) |
374 | 0 | { |
375 | | // |
376 | | // Copy the next scan line from the caller's |
377 | | // frame buffer into _tmpBuf. |
378 | | // |
379 | |
|
380 | 0 | for (int j = 0; j < _width; ++j) |
381 | 0 | { |
382 | 0 | _tmpBuf[j] = _fbBase[_fbYStride * _currentScanLine + |
383 | 0 | _fbXStride * (j + _xMin)]; |
384 | 0 | } |
385 | | |
386 | | // |
387 | | // Convert the scan line from RGB to luminance/chroma, |
388 | | // and store the result in the output file. |
389 | | // |
390 | |
|
391 | 0 | RGBAtoYCA (_yw, _width, _writeA, _tmpBuf, _tmpBuf); |
392 | 0 | _outputFile.writePixels (1); |
393 | |
|
394 | 0 | ++_linesConverted; |
395 | |
|
396 | 0 | if (_lineOrder == INCREASING_Y) |
397 | 0 | ++_currentScanLine; |
398 | 0 | else |
399 | 0 | --_currentScanLine; |
400 | 0 | } |
401 | 0 | } |
402 | 0 | else |
403 | 0 | { |
404 | | // |
405 | | // We are writing chroma; the pixels must be filtered and subsampled. |
406 | | // |
407 | |
|
408 | 0 | for (int i = 0; i < numScanLines; ++i) |
409 | 0 | { |
410 | | // |
411 | | // Copy the next scan line from the caller's |
412 | | // frame buffer into _tmpBuf. |
413 | | // |
414 | |
|
415 | 0 | for (int j = 0; j < _width; ++j) |
416 | 0 | { |
417 | 0 | _tmpBuf[j + N2] = _fbBase[_fbYStride * _currentScanLine + |
418 | 0 | _fbXStride * (j + _xMin)]; |
419 | 0 | } |
420 | | |
421 | | // |
422 | | // Convert the scan line from RGB to luminance/chroma. |
423 | | // |
424 | |
|
425 | 0 | RGBAtoYCA (_yw, _width, _writeA, _tmpBuf + N2, _tmpBuf + N2); |
426 | | |
427 | | // |
428 | | // Append N2 copies of the first and last pixel to the |
429 | | // beginning and end of the scan line. |
430 | | // |
431 | |
|
432 | 0 | padTmpBuf (); |
433 | | |
434 | | // |
435 | | // Filter and subsample the scan line's chroma channels |
436 | | // horizontally; store the result in _buf. |
437 | | // |
438 | |
|
439 | 0 | rotateBuffers(); |
440 | 0 | decimateChromaHoriz (_width, _tmpBuf, _buf[N - 1]); |
441 | | |
442 | | // |
443 | | // If this is the first scan line in the image, |
444 | | // store N2 more copies of the scan line in _buf. |
445 | | // |
446 | |
|
447 | 0 | if (_linesConverted == 0) |
448 | 0 | { |
449 | 0 | for (int j = 0; j < N2; ++j) |
450 | 0 | duplicateLastBuffer(); |
451 | 0 | } |
452 | |
|
453 | 0 | ++_linesConverted; |
454 | | |
455 | | // |
456 | | // If we have have converted at least N2 scan lines from |
457 | | // RGBA to luminance/chroma, then we can start to filter |
458 | | // and subsample vertically, and store pixels in the |
459 | | // output file. |
460 | | // |
461 | |
|
462 | 0 | if (_linesConverted > N2) |
463 | 0 | decimateChromaVertAndWriteScanLine(); |
464 | | |
465 | | // |
466 | | // If we have already converted the last scan line in |
467 | | // the image to luminance/chroma, filter, subsample and |
468 | | // store the remaining scan lines in _buf. |
469 | | // |
470 | |
|
471 | 0 | if (_linesConverted >= _height) |
472 | 0 | { |
473 | 0 | for (int j = 0; j < N2 - _height; ++j) |
474 | 0 | duplicateLastBuffer(); |
475 | |
|
476 | 0 | duplicateSecondToLastBuffer(); |
477 | 0 | ++_linesConverted; |
478 | 0 | decimateChromaVertAndWriteScanLine(); |
479 | |
|
480 | 0 | for (int j = 1; j < min (_height, N2); ++j) |
481 | 0 | { |
482 | 0 | duplicateLastBuffer(); |
483 | 0 | ++_linesConverted; |
484 | 0 | decimateChromaVertAndWriteScanLine(); |
485 | 0 | } |
486 | 0 | } |
487 | |
|
488 | 0 | if (_lineOrder == INCREASING_Y) |
489 | 0 | ++_currentScanLine; |
490 | 0 | else |
491 | 0 | --_currentScanLine; |
492 | 0 | } |
493 | 0 | } |
494 | 0 | } |
495 | | |
496 | | |
497 | | int |
498 | | RgbaOutputFile::ToYca::currentScanLine () const |
499 | 0 | { |
500 | 0 | return _currentScanLine; |
501 | 0 | } |
502 | | |
503 | | |
504 | | void |
505 | | RgbaOutputFile::ToYca::padTmpBuf () |
506 | 0 | { |
507 | 0 | for (int i = 0; i < N2; ++i) |
508 | 0 | { |
509 | 0 | _tmpBuf[i] = _tmpBuf[N2]; |
510 | 0 | _tmpBuf[_width + N2 + i] = _tmpBuf[_width + N2 - 2]; |
511 | 0 | } |
512 | 0 | } |
513 | | |
514 | | |
515 | | void |
516 | | RgbaOutputFile::ToYca::rotateBuffers () |
517 | 0 | { |
518 | 0 | Rgba *tmp = _buf[0]; |
519 | |
|
520 | 0 | for (int i = 0; i < N - 1; ++i) |
521 | 0 | _buf[i] = _buf[i + 1]; |
522 | |
|
523 | 0 | _buf[N - 1] = tmp; |
524 | 0 | } |
525 | | |
526 | | |
527 | | void |
528 | | RgbaOutputFile::ToYca::duplicateLastBuffer () |
529 | 0 | { |
530 | 0 | rotateBuffers(); |
531 | 0 | memcpy (_buf[N - 1], _buf[N - 2], _width * sizeof (Rgba)); |
532 | 0 | } |
533 | | |
534 | | |
535 | | void |
536 | | RgbaOutputFile::ToYca::duplicateSecondToLastBuffer () |
537 | 0 | { |
538 | 0 | rotateBuffers(); |
539 | 0 | memcpy (_buf[N - 1], _buf[N - 3], _width * sizeof (Rgba)); |
540 | 0 | } |
541 | | |
542 | | |
543 | | void |
544 | | RgbaOutputFile::ToYca::decimateChromaVertAndWriteScanLine () |
545 | 0 | { |
546 | 0 | if (_linesConverted & 1) |
547 | 0 | memcpy (_tmpBuf, _buf[N2], _width * sizeof (Rgba)); |
548 | 0 | else |
549 | 0 | decimateChromaVert (_width, _buf, _tmpBuf); |
550 | |
|
551 | 0 | if (_writeY && _writeC) |
552 | 0 | roundYCA (_width, _roundY, _roundC, _tmpBuf, _tmpBuf); |
553 | |
|
554 | 0 | _outputFile.writePixels (1); |
555 | 0 | } |
556 | | |
557 | | |
558 | | RgbaOutputFile::RgbaOutputFile (const char name[], |
559 | | const Header &header, |
560 | | RgbaChannels rgbaChannels, |
561 | | int numThreads): |
562 | | _outputFile (0), |
563 | | _toYca (0) |
564 | 0 | { |
565 | 0 | Header hd (header); |
566 | 0 | insertChannels (hd, rgbaChannels); |
567 | 0 | _outputFile = new OutputFile (name, hd, numThreads); |
568 | |
|
569 | 0 | if (rgbaChannels & (WRITE_Y | WRITE_C)) |
570 | 0 | _toYca = new ToYca (*_outputFile, rgbaChannels); |
571 | 0 | } |
572 | | |
573 | | |
574 | | RgbaOutputFile::RgbaOutputFile (OPENEXR_IMF_INTERNAL_NAMESPACE::OStream &os, |
575 | | const Header &header, |
576 | | RgbaChannels rgbaChannels, |
577 | | int numThreads): |
578 | | _outputFile (0), |
579 | | _toYca (0) |
580 | 0 | { |
581 | 0 | Header hd (header); |
582 | 0 | insertChannels (hd, rgbaChannels); |
583 | 0 | _outputFile = new OutputFile (os, hd, numThreads); |
584 | |
|
585 | 0 | if (rgbaChannels & (WRITE_Y | WRITE_C)) |
586 | 0 | _toYca = new ToYca (*_outputFile, rgbaChannels); |
587 | 0 | } |
588 | | |
589 | | |
590 | | RgbaOutputFile::RgbaOutputFile (const char name[], |
591 | | const IMATH_NAMESPACE::Box2i &displayWindow, |
592 | | const IMATH_NAMESPACE::Box2i &dataWindow, |
593 | | RgbaChannels rgbaChannels, |
594 | | float pixelAspectRatio, |
595 | | const IMATH_NAMESPACE::V2f screenWindowCenter, |
596 | | float screenWindowWidth, |
597 | | LineOrder lineOrder, |
598 | | Compression compression, |
599 | | int numThreads): |
600 | | _outputFile (0), |
601 | | _toYca (0) |
602 | 0 | { |
603 | 0 | Header hd (displayWindow, |
604 | 0 | dataWindow.isEmpty()? displayWindow: dataWindow, |
605 | 0 | pixelAspectRatio, |
606 | 0 | screenWindowCenter, |
607 | 0 | screenWindowWidth, |
608 | 0 | lineOrder, |
609 | 0 | compression); |
610 | |
|
611 | 0 | insertChannels (hd, rgbaChannels); |
612 | 0 | _outputFile = new OutputFile (name, hd, numThreads); |
613 | |
|
614 | 0 | if (rgbaChannels & (WRITE_Y | WRITE_C)) |
615 | 0 | _toYca = new ToYca (*_outputFile, rgbaChannels); |
616 | 0 | } |
617 | | |
618 | | |
619 | | RgbaOutputFile::RgbaOutputFile (const char name[], |
620 | | int width, |
621 | | int height, |
622 | | RgbaChannels rgbaChannels, |
623 | | float pixelAspectRatio, |
624 | | const IMATH_NAMESPACE::V2f screenWindowCenter, |
625 | | float screenWindowWidth, |
626 | | LineOrder lineOrder, |
627 | | Compression compression, |
628 | | int numThreads): |
629 | | _outputFile (0), |
630 | | _toYca (0) |
631 | 0 | { |
632 | 0 | Header hd (width, |
633 | 0 | height, |
634 | 0 | pixelAspectRatio, |
635 | 0 | screenWindowCenter, |
636 | 0 | screenWindowWidth, |
637 | 0 | lineOrder, |
638 | 0 | compression); |
639 | |
|
640 | 0 | insertChannels (hd, rgbaChannels); |
641 | 0 | _outputFile = new OutputFile (name, hd, numThreads); |
642 | |
|
643 | 0 | if (rgbaChannels & (WRITE_Y | WRITE_C)) |
644 | 0 | _toYca = new ToYca (*_outputFile, rgbaChannels); |
645 | 0 | } |
646 | | |
647 | | |
648 | | RgbaOutputFile::~RgbaOutputFile () |
649 | 0 | { |
650 | 0 | delete _toYca; |
651 | 0 | delete _outputFile; |
652 | 0 | } |
653 | | |
654 | | |
655 | | void |
656 | | RgbaOutputFile::setFrameBuffer (const Rgba *base, |
657 | | size_t xStride, |
658 | | size_t yStride) |
659 | 0 | { |
660 | 0 | if (_toYca) |
661 | 0 | { |
662 | 0 | Lock lock (*_toYca); |
663 | 0 | _toYca->setFrameBuffer (base, xStride, yStride); |
664 | 0 | } |
665 | 0 | else |
666 | 0 | { |
667 | 0 | size_t xs = xStride * sizeof (Rgba); |
668 | 0 | size_t ys = yStride * sizeof (Rgba); |
669 | |
|
670 | 0 | FrameBuffer fb; |
671 | |
|
672 | 0 | fb.insert ("R", Slice (HALF, (char *) &base[0].r, xs, ys)); |
673 | 0 | fb.insert ("G", Slice (HALF, (char *) &base[0].g, xs, ys)); |
674 | 0 | fb.insert ("B", Slice (HALF, (char *) &base[0].b, xs, ys)); |
675 | 0 | fb.insert ("A", Slice (HALF, (char *) &base[0].a, xs, ys)); |
676 | |
|
677 | 0 | _outputFile->setFrameBuffer (fb); |
678 | 0 | } |
679 | 0 | } |
680 | | |
681 | | |
682 | | void |
683 | | RgbaOutputFile::writePixels (int numScanLines) |
684 | 0 | { |
685 | 0 | if (_toYca) |
686 | 0 | { |
687 | 0 | Lock lock (*_toYca); |
688 | 0 | _toYca->writePixels (numScanLines); |
689 | 0 | } |
690 | 0 | else |
691 | 0 | { |
692 | 0 | _outputFile->writePixels (numScanLines); |
693 | 0 | } |
694 | 0 | } |
695 | | |
696 | | |
697 | | int |
698 | | RgbaOutputFile::currentScanLine () const |
699 | 0 | { |
700 | 0 | if (_toYca) |
701 | 0 | { |
702 | 0 | Lock lock (*_toYca); |
703 | 0 | return _toYca->currentScanLine(); |
704 | 0 | } |
705 | 0 | else |
706 | 0 | { |
707 | 0 | return _outputFile->currentScanLine(); |
708 | 0 | } |
709 | 0 | } |
710 | | |
711 | | |
712 | | const Header & |
713 | | RgbaOutputFile::header () const |
714 | 0 | { |
715 | 0 | return _outputFile->header(); |
716 | 0 | } |
717 | | |
718 | | |
719 | | const FrameBuffer & |
720 | | RgbaOutputFile::frameBuffer () const |
721 | 0 | { |
722 | 0 | return _outputFile->frameBuffer(); |
723 | 0 | } |
724 | | |
725 | | |
726 | | const IMATH_NAMESPACE::Box2i & |
727 | | RgbaOutputFile::displayWindow () const |
728 | 0 | { |
729 | 0 | return _outputFile->header().displayWindow(); |
730 | 0 | } |
731 | | |
732 | | |
733 | | const IMATH_NAMESPACE::Box2i & |
734 | | RgbaOutputFile::dataWindow () const |
735 | 0 | { |
736 | 0 | return _outputFile->header().dataWindow(); |
737 | 0 | } |
738 | | |
739 | | |
740 | | float |
741 | | RgbaOutputFile::pixelAspectRatio () const |
742 | 0 | { |
743 | 0 | return _outputFile->header().pixelAspectRatio(); |
744 | 0 | } |
745 | | |
746 | | |
747 | | const IMATH_NAMESPACE::V2f |
748 | | RgbaOutputFile::screenWindowCenter () const |
749 | 0 | { |
750 | 0 | return _outputFile->header().screenWindowCenter(); |
751 | 0 | } |
752 | | |
753 | | |
754 | | float |
755 | | RgbaOutputFile::screenWindowWidth () const |
756 | 0 | { |
757 | 0 | return _outputFile->header().screenWindowWidth(); |
758 | 0 | } |
759 | | |
760 | | |
761 | | LineOrder |
762 | | RgbaOutputFile::lineOrder () const |
763 | 0 | { |
764 | 0 | return _outputFile->header().lineOrder(); |
765 | 0 | } |
766 | | |
767 | | |
768 | | Compression |
769 | | RgbaOutputFile::compression () const |
770 | 0 | { |
771 | 0 | return _outputFile->header().compression(); |
772 | 0 | } |
773 | | |
774 | | |
775 | | RgbaChannels |
776 | | RgbaOutputFile::channels () const |
777 | 0 | { |
778 | 0 | return rgbaChannels (_outputFile->header().channels()); |
779 | 0 | } |
780 | | |
781 | | |
782 | | void |
783 | | RgbaOutputFile::updatePreviewImage (const PreviewRgba newPixels[]) |
784 | 0 | { |
785 | 0 | _outputFile->updatePreviewImage (newPixels); |
786 | 0 | } |
787 | | |
788 | | |
789 | | void |
790 | | RgbaOutputFile::setYCRounding (unsigned int roundY, unsigned int roundC) |
791 | 0 | { |
792 | 0 | if (_toYca) |
793 | 0 | { |
794 | 0 | Lock lock (*_toYca); |
795 | 0 | _toYca->setYCRounding (roundY, roundC); |
796 | 0 | } |
797 | 0 | } |
798 | | |
799 | | |
800 | | void |
801 | | RgbaOutputFile::breakScanLine (int y, int offset, int length, char c) |
802 | 0 | { |
803 | 0 | _outputFile->breakScanLine (y, offset, length, c); |
804 | 0 | } |
805 | | |
806 | | |
807 | | class RgbaInputFile::FromYca: public Mutex |
808 | | { |
809 | | public: |
810 | | |
811 | | FromYca (InputFile &inputFile, RgbaChannels rgbaChannels); |
812 | | ~FromYca (); |
813 | | |
814 | | void setFrameBuffer (Rgba *base, |
815 | | size_t xStride, |
816 | | size_t yStride, |
817 | | const string &channelNamePrefix); |
818 | | |
819 | | void readPixels (int scanLine1, int scanLine2); |
820 | | |
821 | | private: |
822 | | |
823 | | void readPixels (int scanLine); |
824 | | void rotateBuf1 (int d); |
825 | | void rotateBuf2 (int d); |
826 | | void readYCAScanLine (int y, Rgba buf[]); |
827 | | void padTmpBuf (); |
828 | | |
829 | | InputFile & _inputFile; |
830 | | bool _readC; |
831 | | int _xMin; |
832 | | int _yMin; |
833 | | int _yMax; |
834 | | int _width; |
835 | | int _height; |
836 | | int _currentScanLine; |
837 | | LineOrder _lineOrder; |
838 | | V3f _yw; |
839 | | Rgba * _bufBase; |
840 | | Rgba * _buf1[N + 2]; |
841 | | Rgba * _buf2[3]; |
842 | | Rgba * _tmpBuf; |
843 | | Rgba * _fbBase; |
844 | | size_t _fbXStride; |
845 | | size_t _fbYStride; |
846 | | }; |
847 | | |
848 | | |
849 | | RgbaInputFile::FromYca::FromYca (InputFile &inputFile, |
850 | | RgbaChannels rgbaChannels) |
851 | | : |
852 | | _inputFile (inputFile) |
853 | 0 | { |
854 | 0 | _readC = (rgbaChannels & WRITE_C)? true: false; |
855 | |
|
856 | 0 | const Box2i dw = _inputFile.header().dataWindow(); |
857 | |
|
858 | 0 | _xMin = dw.min.x; |
859 | 0 | _yMin = dw.min.y; |
860 | 0 | _yMax = dw.max.y; |
861 | 0 | _width = dw.max.x - dw.min.x + 1; |
862 | 0 | _height = dw.max.y - dw.min.y + 1; |
863 | 0 | _currentScanLine = dw.min.y - N - 2; |
864 | 0 | _lineOrder = _inputFile.header().lineOrder(); |
865 | 0 | _yw = ywFromHeader (_inputFile.header()); |
866 | |
|
867 | 0 | ptrdiff_t pad = cachePadding (_width * sizeof (Rgba)) / sizeof (Rgba); |
868 | |
|
869 | 0 | _bufBase = new Rgba[(_width + pad) * (N + 2 + 3)]; |
870 | |
|
871 | 0 | for (int i = 0; i < N + 2; ++i) |
872 | 0 | _buf1[i] = _bufBase + (i * (_width + pad)); |
873 | | |
874 | 0 | for (int i = 0; i < 3; ++i) |
875 | 0 | _buf2[i] = _bufBase + ((i + N + 2) * (_width + pad)); |
876 | |
|
877 | 0 | _tmpBuf = new Rgba[_width + N - 1]; |
878 | |
|
879 | 0 | _fbBase = 0; |
880 | 0 | _fbXStride = 0; |
881 | 0 | _fbYStride = 0; |
882 | 0 | } |
883 | | |
884 | | |
885 | | RgbaInputFile::FromYca::~FromYca () |
886 | 0 | { |
887 | 0 | delete [] _bufBase; |
888 | 0 | delete [] _tmpBuf; |
889 | 0 | } |
890 | | |
891 | | |
892 | | void |
893 | | RgbaInputFile::FromYca::setFrameBuffer (Rgba *base, |
894 | | size_t xStride, |
895 | | size_t yStride, |
896 | | const string &channelNamePrefix) |
897 | 0 | { |
898 | 0 | if (_fbBase == 0) |
899 | 0 | { |
900 | 0 | FrameBuffer fb; |
901 | |
|
902 | 0 | fb.insert (channelNamePrefix + "Y", |
903 | 0 | Slice (HALF, // type |
904 | 0 | (char *) &_tmpBuf[N2 - _xMin].g, // base |
905 | 0 | sizeof (Rgba), // xStride |
906 | 0 | 0, // yStride |
907 | 0 | 1, // xSampling |
908 | 0 | 1, // ySampling |
909 | 0 | 0.5)); // fillValue |
910 | |
|
911 | 0 | if (_readC) |
912 | 0 | { |
913 | 0 | fb.insert (channelNamePrefix + "RY", |
914 | 0 | Slice (HALF, // type |
915 | 0 | (char *) &_tmpBuf[N2 - _xMin].r, // base |
916 | 0 | sizeof (Rgba) * 2, // xStride |
917 | 0 | 0, // yStride |
918 | 0 | 2, // xSampling |
919 | 0 | 2, // ySampling |
920 | 0 | 0.0)); // fillValue |
921 | |
|
922 | 0 | fb.insert (channelNamePrefix + "BY", |
923 | 0 | Slice (HALF, // type |
924 | 0 | (char *) &_tmpBuf[N2 - _xMin].b, // base |
925 | 0 | sizeof (Rgba) * 2, // xStride |
926 | 0 | 0, // yStride |
927 | 0 | 2, // xSampling |
928 | 0 | 2, // ySampling |
929 | 0 | 0.0)); // fillValue |
930 | 0 | } |
931 | |
|
932 | 0 | fb.insert (channelNamePrefix + "A", |
933 | 0 | Slice (HALF, // type |
934 | 0 | (char *) &_tmpBuf[N2 - _xMin].a, // base |
935 | 0 | sizeof (Rgba), // xStride |
936 | 0 | 0, // yStride |
937 | 0 | 1, // xSampling |
938 | 0 | 1, // ySampling |
939 | 0 | 1.0)); // fillValue |
940 | |
|
941 | 0 | _inputFile.setFrameBuffer (fb); |
942 | 0 | } |
943 | |
|
944 | 0 | _fbBase = base; |
945 | 0 | _fbXStride = xStride; |
946 | 0 | _fbYStride = yStride; |
947 | 0 | } |
948 | | |
949 | | |
950 | | void |
951 | | RgbaInputFile::FromYca::readPixels (int scanLine1, int scanLine2) |
952 | 0 | { |
953 | 0 | int minY = min (scanLine1, scanLine2); |
954 | 0 | int maxY = max (scanLine1, scanLine2); |
955 | |
|
956 | 0 | if (_lineOrder == INCREASING_Y) |
957 | 0 | { |
958 | 0 | for (int y = minY; y <= maxY; ++y) |
959 | 0 | readPixels (y); |
960 | 0 | } |
961 | 0 | else |
962 | 0 | { |
963 | 0 | for (int y = maxY; y >= minY; --y) |
964 | 0 | readPixels (y); |
965 | 0 | } |
966 | 0 | } |
967 | | |
968 | | |
969 | | void |
970 | | RgbaInputFile::FromYca::readPixels (int scanLine) |
971 | 0 | { |
972 | 0 | if (_fbBase == 0) |
973 | 0 | { |
974 | 0 | THROW (IEX_NAMESPACE::ArgExc, "No frame buffer was specified as the " |
975 | 0 | "pixel data destination for image file " |
976 | 0 | "\"" << _inputFile.fileName() << "\"."); |
977 | 0 | } |
978 | | |
979 | | // |
980 | | // In order to convert one scan line to RGB format, we need that |
981 | | // scan line plus N2+1 extra scan lines above and N2+1 scan lines |
982 | | // below in luminance/chroma format. |
983 | | // |
984 | | // We allow random access to scan lines, but we buffer partially |
985 | | // processed luminance/chroma data in order to make reading pixels |
986 | | // in increasing y or decreasing y order reasonably efficient: |
987 | | // |
988 | | // _currentScanLine holds the y coordinate of the scan line |
989 | | // that was most recently read. |
990 | | // |
991 | | // _buf1 contains scan lines _currentScanLine-N2-1 |
992 | | // through _currentScanLine+N2+1 in |
993 | | // luminance/chroma format. Odd-numbered |
994 | | // lines contain no chroma data. Even-numbered |
995 | | // lines have valid chroma data for all pixels. |
996 | | // |
997 | | // _buf2 contains scan lines _currentScanLine-1 |
998 | | // through _currentScanLine+1, in RGB format. |
999 | | // Super-saturated pixels (see ImfRgbaYca.h) |
1000 | | // have not yet been eliminated. |
1001 | | // |
1002 | | // If the scan line we are trying to read now is close enough to |
1003 | | // _currentScanLine, we don't have to recompute the contents of _buf1 |
1004 | | // and _buf2 from scratch. We can rotate _buf1 and _buf2, and fill |
1005 | | // in the missing data. |
1006 | | // |
1007 | | |
1008 | 0 | int dy = scanLine - _currentScanLine; |
1009 | |
|
1010 | 0 | if (abs (dy) < N + 2) |
1011 | 0 | rotateBuf1 (dy); |
1012 | |
|
1013 | 0 | if (abs (dy) < 3) |
1014 | 0 | rotateBuf2 (dy); |
1015 | |
|
1016 | 0 | if (dy < 0) |
1017 | 0 | { |
1018 | 0 | { |
1019 | 0 | int n = min (-dy, N + 2); |
1020 | 0 | int yMin = scanLine - N2 - 1; |
1021 | |
|
1022 | 0 | for (int i = n - 1; i >= 0; --i) |
1023 | 0 | readYCAScanLine (yMin + i, _buf1[i]); |
1024 | 0 | } |
1025 | |
|
1026 | 0 | { |
1027 | 0 | int n = min (-dy, 3); |
1028 | |
|
1029 | 0 | for (int i = 0; i < n; ++i) |
1030 | 0 | { |
1031 | 0 | if ((scanLine + i) & 1) |
1032 | 0 | { |
1033 | 0 | YCAtoRGBA (_yw, _width, _buf1[N2 + i], _buf2[i]); |
1034 | 0 | } |
1035 | 0 | else |
1036 | 0 | { |
1037 | 0 | reconstructChromaVert (_width, _buf1 + i, _buf2[i]); |
1038 | 0 | YCAtoRGBA (_yw, _width, _buf2[i], _buf2[i]); |
1039 | 0 | } |
1040 | 0 | } |
1041 | 0 | } |
1042 | 0 | } |
1043 | 0 | else |
1044 | 0 | { |
1045 | 0 | { |
1046 | 0 | int n = min (dy, N + 2); |
1047 | 0 | int yMax = scanLine + N2 + 1; |
1048 | |
|
1049 | 0 | for (int i = n - 1; i >= 0; --i) |
1050 | 0 | readYCAScanLine (yMax - i, _buf1[N + 1 - i]); |
1051 | 0 | } |
1052 | |
|
1053 | 0 | { |
1054 | 0 | int n = min (dy, 3); |
1055 | |
|
1056 | 0 | for (int i = 2; i > 2 - n; --i) |
1057 | 0 | { |
1058 | 0 | if ((scanLine + i) & 1) |
1059 | 0 | { |
1060 | 0 | YCAtoRGBA (_yw, _width, _buf1[N2 + i], _buf2[i]); |
1061 | 0 | } |
1062 | 0 | else |
1063 | 0 | { |
1064 | 0 | reconstructChromaVert (_width, _buf1 + i, _buf2[i]); |
1065 | 0 | YCAtoRGBA (_yw, _width, _buf2[i], _buf2[i]); |
1066 | 0 | } |
1067 | 0 | } |
1068 | 0 | } |
1069 | 0 | } |
1070 | |
|
1071 | 0 | fixSaturation (_yw, _width, _buf2, _tmpBuf); |
1072 | |
|
1073 | 0 | for (int i = 0; i < _width; ++i) |
1074 | 0 | _fbBase[_fbYStride * scanLine + _fbXStride * (i + _xMin)] = _tmpBuf[i]; |
1075 | |
|
1076 | 0 | _currentScanLine = scanLine; |
1077 | 0 | } |
1078 | | |
1079 | | |
1080 | | void |
1081 | | RgbaInputFile::FromYca::rotateBuf1 (int d) |
1082 | 0 | { |
1083 | 0 | d = modp (d, N + 2); |
1084 | |
|
1085 | 0 | Rgba *tmp[N + 2]; |
1086 | |
|
1087 | 0 | for (int i = 0; i < N + 2; ++i) |
1088 | 0 | tmp[i] = _buf1[i]; |
1089 | |
|
1090 | 0 | for (int i = 0; i < N + 2; ++i) |
1091 | 0 | _buf1[i] = tmp[(i + d) % (N + 2)]; |
1092 | 0 | } |
1093 | | |
1094 | | |
1095 | | void |
1096 | | RgbaInputFile::FromYca::rotateBuf2 (int d) |
1097 | 0 | { |
1098 | 0 | d = modp (d, 3); |
1099 | |
|
1100 | 0 | Rgba *tmp[3]; |
1101 | |
|
1102 | 0 | for (int i = 0; i < 3; ++i) |
1103 | 0 | tmp[i] = _buf2[i]; |
1104 | |
|
1105 | 0 | for (int i = 0; i < 3; ++i) |
1106 | 0 | _buf2[i] = tmp[(i + d) % 3]; |
1107 | 0 | } |
1108 | | |
1109 | | |
1110 | | void |
1111 | | RgbaInputFile::FromYca::readYCAScanLine (int y, Rgba *buf) |
1112 | 0 | { |
1113 | | // |
1114 | | // Clamp y. |
1115 | | // |
1116 | |
|
1117 | 0 | if (y < _yMin) |
1118 | 0 | y = _yMin; |
1119 | 0 | else if (y > _yMax) |
1120 | 0 | y = _yMax - 1; |
1121 | | |
1122 | | // |
1123 | | // Read scan line y into _tmpBuf. |
1124 | | // |
1125 | |
|
1126 | 0 | _inputFile.readPixels (y); |
1127 | | |
1128 | | // |
1129 | | // Reconstruct missing chroma samples and copy |
1130 | | // the scan line into buf. |
1131 | | // |
1132 | |
|
1133 | 0 | if (!_readC) |
1134 | 0 | { |
1135 | 0 | for (int i = 0; i < _width; ++i) |
1136 | 0 | { |
1137 | 0 | _tmpBuf[i + N2].r = 0; |
1138 | 0 | _tmpBuf[i + N2].b = 0; |
1139 | 0 | } |
1140 | 0 | } |
1141 | |
|
1142 | 0 | if (y & 1) |
1143 | 0 | { |
1144 | 0 | memcpy (buf, _tmpBuf + N2, _width * sizeof (Rgba)); |
1145 | 0 | } |
1146 | 0 | else |
1147 | 0 | { |
1148 | 0 | padTmpBuf(); |
1149 | 0 | reconstructChromaHoriz (_width, _tmpBuf, buf); |
1150 | 0 | } |
1151 | 0 | } |
1152 | | |
1153 | | |
1154 | | void |
1155 | | RgbaInputFile::FromYca::padTmpBuf () |
1156 | 0 | { |
1157 | 0 | for (int i = 0; i < N2; ++i) |
1158 | 0 | { |
1159 | 0 | _tmpBuf[i] = _tmpBuf[N2]; |
1160 | 0 | _tmpBuf[_width + N2 + i] = _tmpBuf[_width + N2 - 2]; |
1161 | 0 | } |
1162 | 0 | } |
1163 | | |
1164 | | |
1165 | | RgbaInputFile::RgbaInputFile (const char name[], int numThreads): |
1166 | | _inputFile (new InputFile (name, numThreads)), |
1167 | | _fromYca (0), |
1168 | | _channelNamePrefix ("") |
1169 | 0 | { |
1170 | 0 | RgbaChannels rgbaChannels = channels(); |
1171 | |
|
1172 | 0 | if (rgbaChannels & (WRITE_Y | WRITE_C)) |
1173 | 0 | _fromYca = new FromYca (*_inputFile, rgbaChannels); |
1174 | 0 | } |
1175 | | |
1176 | | |
1177 | | RgbaInputFile::RgbaInputFile (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is, int numThreads): |
1178 | | _inputFile (new InputFile (is, numThreads)), |
1179 | | _fromYca (0), |
1180 | | _channelNamePrefix ("") |
1181 | 0 | { |
1182 | 0 | RgbaChannels rgbaChannels = channels(); |
1183 | |
|
1184 | 0 | if (rgbaChannels & (WRITE_Y | WRITE_C)) |
1185 | 0 | _fromYca = new FromYca (*_inputFile, rgbaChannels); |
1186 | 0 | } |
1187 | | |
1188 | | |
1189 | | RgbaInputFile::RgbaInputFile (const char name[], |
1190 | | const string &layerName, |
1191 | | int numThreads) |
1192 | | : |
1193 | | _inputFile (new InputFile (name, numThreads)), |
1194 | | _fromYca (0), |
1195 | | _channelNamePrefix (prefixFromLayerName (layerName, _inputFile->header())) |
1196 | 0 | { |
1197 | 0 | RgbaChannels rgbaChannels = channels(); |
1198 | |
|
1199 | 0 | if (rgbaChannels & (WRITE_Y | WRITE_C)) |
1200 | 0 | _fromYca = new FromYca (*_inputFile, rgbaChannels); |
1201 | 0 | } |
1202 | | |
1203 | | |
1204 | | RgbaInputFile::RgbaInputFile (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is, |
1205 | | const string &layerName, |
1206 | | int numThreads) |
1207 | | : |
1208 | | _inputFile (new InputFile (is, numThreads)), |
1209 | | _fromYca (0), |
1210 | | _channelNamePrefix (prefixFromLayerName (layerName, _inputFile->header())) |
1211 | 0 | { |
1212 | 0 | RgbaChannels rgbaChannels = channels(); |
1213 | |
|
1214 | 0 | if (rgbaChannels & (WRITE_Y | WRITE_C)) |
1215 | 0 | _fromYca = new FromYca (*_inputFile, rgbaChannels); |
1216 | 0 | } |
1217 | | |
1218 | | |
1219 | | RgbaInputFile::~RgbaInputFile () |
1220 | 0 | { |
1221 | 0 | delete _inputFile; |
1222 | 0 | delete _fromYca; |
1223 | 0 | } |
1224 | | |
1225 | | |
1226 | | void |
1227 | | RgbaInputFile::setFrameBuffer (Rgba *base, size_t xStride, size_t yStride) |
1228 | 0 | { |
1229 | 0 | if (_fromYca) |
1230 | 0 | { |
1231 | 0 | Lock lock (*_fromYca); |
1232 | 0 | _fromYca->setFrameBuffer (base, xStride, yStride, _channelNamePrefix); |
1233 | 0 | } |
1234 | 0 | else |
1235 | 0 | { |
1236 | 0 | size_t xs = xStride * sizeof (Rgba); |
1237 | 0 | size_t ys = yStride * sizeof (Rgba); |
1238 | |
|
1239 | 0 | FrameBuffer fb; |
1240 | |
|
1241 | 0 | fb.insert (_channelNamePrefix + "R", |
1242 | 0 | Slice (HALF, |
1243 | 0 | (char *) &base[0].r, |
1244 | 0 | xs, ys, |
1245 | 0 | 1, 1, // xSampling, ySampling |
1246 | 0 | 0.0)); // fillValue |
1247 | |
|
1248 | 0 | fb.insert (_channelNamePrefix + "G", |
1249 | 0 | Slice (HALF, |
1250 | 0 | (char *) &base[0].g, |
1251 | 0 | xs, ys, |
1252 | 0 | 1, 1, // xSampling, ySampling |
1253 | 0 | 0.0)); // fillValue |
1254 | |
|
1255 | 0 | fb.insert (_channelNamePrefix + "B", |
1256 | 0 | Slice (HALF, |
1257 | 0 | (char *) &base[0].b, |
1258 | 0 | xs, ys, |
1259 | 0 | 1, 1, // xSampling, ySampling |
1260 | 0 | 0.0)); // fillValue |
1261 | |
|
1262 | 0 | fb.insert (_channelNamePrefix + "A", |
1263 | 0 | Slice (HALF, |
1264 | 0 | (char *) &base[0].a, |
1265 | 0 | xs, ys, |
1266 | 0 | 1, 1, // xSampling, ySampling |
1267 | 0 | 1.0)); // fillValue |
1268 | |
|
1269 | 0 | _inputFile->setFrameBuffer (fb); |
1270 | 0 | } |
1271 | 0 | } |
1272 | | |
1273 | | |
1274 | | void |
1275 | | RgbaInputFile::setLayerName (const string &layerName) |
1276 | 0 | { |
1277 | 0 | delete _fromYca; |
1278 | 0 | _fromYca = 0; |
1279 | |
|
1280 | 0 | _channelNamePrefix = prefixFromLayerName (layerName, _inputFile->header()); |
1281 | |
|
1282 | 0 | RgbaChannels rgbaChannels = channels(); |
1283 | |
|
1284 | 0 | if (rgbaChannels & (WRITE_Y | WRITE_C)) |
1285 | 0 | _fromYca = new FromYca (*_inputFile, rgbaChannels); |
1286 | |
|
1287 | 0 | FrameBuffer fb; |
1288 | 0 | _inputFile->setFrameBuffer (fb); |
1289 | 0 | } |
1290 | | |
1291 | | |
1292 | | void |
1293 | | RgbaInputFile::readPixels (int scanLine1, int scanLine2) |
1294 | 0 | { |
1295 | 0 | if (_fromYca) |
1296 | 0 | { |
1297 | 0 | Lock lock (*_fromYca); |
1298 | 0 | _fromYca->readPixels (scanLine1, scanLine2); |
1299 | 0 | } |
1300 | 0 | else |
1301 | 0 | { |
1302 | 0 | _inputFile->readPixels (scanLine1, scanLine2); |
1303 | 0 | } |
1304 | 0 | } |
1305 | | |
1306 | | |
1307 | | void |
1308 | | RgbaInputFile::readPixels (int scanLine) |
1309 | 0 | { |
1310 | 0 | readPixels (scanLine, scanLine); |
1311 | 0 | } |
1312 | | |
1313 | | |
1314 | | bool |
1315 | | RgbaInputFile::isComplete () const |
1316 | 0 | { |
1317 | 0 | return _inputFile->isComplete(); |
1318 | 0 | } |
1319 | | |
1320 | | |
1321 | | const Header & |
1322 | | RgbaInputFile::header () const |
1323 | 0 | { |
1324 | 0 | return _inputFile->header(); |
1325 | 0 | } |
1326 | | |
1327 | | |
1328 | | const char * |
1329 | | RgbaInputFile::fileName () const |
1330 | 0 | { |
1331 | 0 | return _inputFile->fileName(); |
1332 | 0 | } |
1333 | | |
1334 | | |
1335 | | const FrameBuffer & |
1336 | | RgbaInputFile::frameBuffer () const |
1337 | 0 | { |
1338 | 0 | return _inputFile->frameBuffer(); |
1339 | 0 | } |
1340 | | |
1341 | | |
1342 | | const IMATH_NAMESPACE::Box2i & |
1343 | | RgbaInputFile::displayWindow () const |
1344 | 0 | { |
1345 | 0 | return _inputFile->header().displayWindow(); |
1346 | 0 | } |
1347 | | |
1348 | | |
1349 | | const IMATH_NAMESPACE::Box2i & |
1350 | | RgbaInputFile::dataWindow () const |
1351 | 0 | { |
1352 | 0 | return _inputFile->header().dataWindow(); |
1353 | 0 | } |
1354 | | |
1355 | | |
1356 | | float |
1357 | | RgbaInputFile::pixelAspectRatio () const |
1358 | 0 | { |
1359 | 0 | return _inputFile->header().pixelAspectRatio(); |
1360 | 0 | } |
1361 | | |
1362 | | |
1363 | | const IMATH_NAMESPACE::V2f |
1364 | | RgbaInputFile::screenWindowCenter () const |
1365 | 0 | { |
1366 | 0 | return _inputFile->header().screenWindowCenter(); |
1367 | 0 | } |
1368 | | |
1369 | | |
1370 | | float |
1371 | | RgbaInputFile::screenWindowWidth () const |
1372 | 0 | { |
1373 | 0 | return _inputFile->header().screenWindowWidth(); |
1374 | 0 | } |
1375 | | |
1376 | | |
1377 | | LineOrder |
1378 | | RgbaInputFile::lineOrder () const |
1379 | 0 | { |
1380 | 0 | return _inputFile->header().lineOrder(); |
1381 | 0 | } |
1382 | | |
1383 | | |
1384 | | Compression |
1385 | | RgbaInputFile::compression () const |
1386 | 0 | { |
1387 | 0 | return _inputFile->header().compression(); |
1388 | 0 | } |
1389 | | |
1390 | | |
1391 | | RgbaChannels |
1392 | | RgbaInputFile::channels () const |
1393 | 0 | { |
1394 | 0 | return rgbaChannels (_inputFile->header().channels(), _channelNamePrefix); |
1395 | 0 | } |
1396 | | |
1397 | | |
1398 | | int |
1399 | | RgbaInputFile::version () const |
1400 | 0 | { |
1401 | 0 | return _inputFile->version(); |
1402 | 0 | } |
1403 | | |
1404 | | |
1405 | | OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT |