1 // Copyright 2008-present Contributors to the OpenImageIO project.
2 // SPDX-License-Identifier: BSD-3-Clause
3 // https://github.com/OpenImageIO/oiio/blob/master/LICENSE.md
4
5 #include <cmath>
6 #include <iomanip>
7 #include <memory>
8
9 #include <OpenEXR/ImfTimeCode.h> //For TimeCode support
10
11 // Note: libdpx originally from: https://github.com/PatrickPalmer/dpx
12 // But that seems not to be actively maintained.
13 #include "libdpx/DPX.h"
14 #include "libdpx/DPXColorConverter.h"
15
16 #include <OpenImageIO/imageio.h>
17 #include <OpenImageIO/strutil.h>
18 #include <OpenImageIO/typedesc.h>
19
20 OIIO_PLUGIN_NAMESPACE_BEGIN
21
22
23 class DPXInput final : public ImageInput {
24 public:
DPXInput()25 DPXInput() { init(); }
~DPXInput()26 virtual ~DPXInput() { close(); }
format_name(void) const27 virtual const char* format_name(void) const override { return "dpx"; }
supports(string_view feature) const28 virtual int supports(string_view feature) const override
29 {
30 return (feature == "ioproxy");
31 }
32 virtual bool valid_file(const std::string& filename) const override;
33 virtual bool open(const std::string& name, ImageSpec& newspec) override;
34 virtual bool open(const std::string& name, ImageSpec& newspec,
35 const ImageSpec& config) override;
36 virtual bool close() override;
current_subimage(void) const37 virtual int current_subimage(void) const override { return m_subimage; }
38 virtual bool seek_subimage(int subimage, int miplevel) override;
39 virtual bool read_native_scanline(int subimage, int miplevel, int y, int z,
40 void* data) override;
41 virtual bool read_native_scanlines(int subimage, int miplevel, int ybegin,
42 int yend, int z, void* data) override;
set_ioproxy(Filesystem::IOProxy * ioproxy)43 virtual bool set_ioproxy(Filesystem::IOProxy* ioproxy) override
44 {
45 m_io = ioproxy;
46 return true;
47 }
48
49 private:
50 int m_subimage;
51 InStream* m_stream = nullptr;
52 dpx::Reader m_dpx;
53 std::vector<unsigned char> m_userBuf;
54 bool m_rawcolor;
55 std::vector<unsigned char> m_decodebuf; // temporary decode buffer
56 std::unique_ptr<Filesystem::IOProxy> m_io_local;
57 Filesystem::IOProxy* m_io = nullptr;
58 int64_t m_io_offset = 0;
59
60 /// Reset everything to initial state
61 ///
init()62 void init()
63 {
64 m_subimage = -1;
65 if (m_stream) {
66 delete m_stream;
67 m_stream = nullptr;
68 m_dpx.SetInStream(nullptr);
69 }
70 m_userBuf.clear();
71 m_rawcolor = false;
72 m_io = nullptr;
73 }
74
75 /// Helper function - retrieve string for libdpx characteristic
76 ///
77 std::string get_characteristic_string(dpx::Characteristic c);
78
79 /// Helper function - retrieve string for libdpx descriptor
80 ///
81 std::string get_descriptor_string(dpx::Descriptor c);
82
83 /// Helper function - fill int array with KeyCode values
84 ///
85 void get_keycode_values(int* array);
86
87 /// Helper function - convert Imf::TimeCode to string;
88 ///
89 std::string get_timecode_string(Imf::TimeCode& tc);
90 };
91
92
93
94 // Obligatory material to make this a recognizeable imageio plugin:
95 OIIO_PLUGIN_EXPORTS_BEGIN
96
97 OIIO_EXPORT ImageInput*
dpx_input_imageio_create()98 dpx_input_imageio_create()
99 {
100 return new DPXInput;
101 }
102
103 OIIO_EXPORT int dpx_imageio_version = OIIO_PLUGIN_VERSION;
104
105 OIIO_EXPORT const char*
dpx_imageio_library_version()106 dpx_imageio_library_version()
107 {
108 return nullptr;
109 }
110
111 OIIO_EXPORT const char* dpx_input_extensions[] = { "dpx", nullptr };
112
113 OIIO_PLUGIN_EXPORTS_END
114
115
116
117 bool
valid_file(const std::string & filename) const118 DPXInput::valid_file(const std::string& filename) const
119 {
120 Filesystem::IOProxy* io
121 = new Filesystem::IOFile(filename, Filesystem::IOProxy::Mode::Read);
122 std::unique_ptr<Filesystem::IOProxy> io_uptr(io);
123 if (!io || io->mode() != Filesystem::IOProxy::Mode::Read)
124 return false;
125
126 std::unique_ptr<InStream> stream_uptr(new InStream(io));
127 if (!stream_uptr)
128 return false;
129
130 dpx::Reader dpx;
131 dpx.SetInStream(stream_uptr.get());
132 return dpx.ReadHeader(); // IOFile is automatically closed when destructed
133 }
134
135
136
137 bool
open(const std::string & name,ImageSpec & newspec)138 DPXInput::open(const std::string& name, ImageSpec& newspec)
139 {
140 if (!m_io) {
141 // If no proxy was supplied, create a file reader
142 m_io = new Filesystem::IOFile(name, Filesystem::IOProxy::Mode::Read);
143 m_io_local.reset(m_io);
144 }
145 if (!m_io || m_io->mode() != Filesystem::IOProxy::Mode::Read) {
146 errorf("Could not open file \"%s\"", name);
147 return false;
148 }
149 m_io_offset = m_io->tell();
150
151 m_stream = new InStream(m_io);
152 if (!m_stream) {
153 errorf("Could not open file \"%s\"", name);
154 return false;
155 }
156
157 m_dpx.SetInStream(m_stream);
158 if (!m_dpx.ReadHeader()) {
159 errorf("Could not read header");
160 close();
161 return false;
162 }
163
164 bool ok = seek_subimage(0, 0);
165 if (ok)
166 newspec = spec();
167 else
168 close();
169 return ok;
170 }
171
172
173
174 bool
open(const std::string & name,ImageSpec & newspec,const ImageSpec & config)175 DPXInput::open(const std::string& name, ImageSpec& newspec,
176 const ImageSpec& config)
177 {
178 // Check 'config' for any special requests
179 m_rawcolor = config.get_int_attribute("dpx:RawColor")
180 || config.get_int_attribute("dpx:RawData") // deprecated
181 || config.get_int_attribute("oiio:RawColor");
182 auto ioparam = config.find_attribute("oiio:ioproxy", TypeDesc::PTR);
183 if (ioparam)
184 m_io = ioparam->get<Filesystem::IOProxy*>();
185 return open(name, newspec);
186 }
187
188
189
190 bool
seek_subimage(int subimage,int miplevel)191 DPXInput::seek_subimage(int subimage, int miplevel)
192 {
193 if (miplevel != 0)
194 return false;
195 if (subimage == m_subimage)
196 return true;
197 if (subimage < 0 || subimage >= m_dpx.header.ImageElementCount())
198 return false;
199
200 m_subimage = subimage;
201
202 // create imagespec
203 TypeDesc typedesc;
204 switch (m_dpx.header.ComponentDataSize(subimage)) {
205 case dpx::kByte:
206 typedesc = m_dpx.header.DataSign(subimage) ? TypeDesc::INT8
207 : TypeDesc::UINT8;
208 break;
209 case dpx::kWord:
210 typedesc = m_dpx.header.DataSign(subimage) ? TypeDesc::INT16
211 : TypeDesc::UINT16;
212 break;
213 case dpx::kInt:
214 typedesc = m_dpx.header.DataSign(subimage) ? TypeDesc::INT32
215 : TypeDesc::UINT32;
216 break;
217 case dpx::kFloat: typedesc = TypeDesc::FLOAT; break;
218 case dpx::kDouble: typedesc = TypeDesc::DOUBLE; break;
219 default: errorf("Invalid component data size"); return false;
220 }
221 m_spec = ImageSpec(m_dpx.header.Width(), m_dpx.header.Height(),
222 m_dpx.header.ImageElementComponentCount(subimage),
223 typedesc);
224
225 // xOffset/yOffset are defined as unsigned 32-bit integers, but m_spec.x/y are signed
226 // avoid casts that would result in negative values
227 if (m_dpx.header.xOffset <= (unsigned int)std::numeric_limits<int>::max())
228 m_spec.x = m_dpx.header.xOffset;
229 if (m_dpx.header.yOffset <= (unsigned int)std::numeric_limits<int>::max())
230 m_spec.y = m_dpx.header.yOffset;
231 if ((int)m_dpx.header.xOriginalSize > 0)
232 m_spec.full_width = m_dpx.header.xOriginalSize;
233 if ((int)m_dpx.header.yOriginalSize > 0)
234 m_spec.full_height = m_dpx.header.yOriginalSize;
235
236 // fill channel names
237 m_spec.channelnames.clear();
238 switch (m_dpx.header.ImageDescriptor(subimage)) {
239 /*case dpx::kUserDefinedDescriptor:
240 break;*/
241 case dpx::kRed: m_spec.channelnames.emplace_back("R"); break;
242 case dpx::kGreen: m_spec.channelnames.emplace_back("G"); break;
243 case dpx::kBlue: m_spec.channelnames.emplace_back("B"); break;
244 case dpx::kAlpha:
245 m_spec.channelnames.emplace_back("A");
246 m_spec.alpha_channel = 0;
247 break;
248 case dpx::kLuma: m_spec.channelnames.emplace_back("Y"); break;
249 case dpx::kDepth:
250 m_spec.channelnames.emplace_back("Z");
251 m_spec.z_channel = 0;
252 break;
253 /*case dpx::kCompositeVideo:
254 break;*/
255 case dpx::kRGB:
256 case dpx::kRGBA:
257 case dpx::kABGR: // colour converter will swap the bytes for us
258 m_spec.default_channel_names();
259 break;
260 case dpx::kCbYCrY:
261 if (m_rawcolor) {
262 m_spec.channelnames.emplace_back("CbCr");
263 m_spec.channelnames.emplace_back("Y");
264 } else {
265 m_spec.nchannels = 3;
266 m_spec.default_channel_names();
267 }
268 break;
269 case dpx::kCbYACrYA:
270 if (m_rawcolor) {
271 m_spec.channelnames.emplace_back("CbCr");
272 m_spec.channelnames.emplace_back("Y");
273 m_spec.channelnames.emplace_back("A");
274 m_spec.alpha_channel = 2;
275 } else {
276 m_spec.nchannels = 4;
277 m_spec.default_channel_names();
278 }
279 break;
280 case dpx::kCbYCr:
281 if (m_rawcolor) {
282 m_spec.channelnames.emplace_back("Cb");
283 m_spec.channelnames.emplace_back("Y");
284 m_spec.channelnames.emplace_back("Cr");
285 } else
286 m_spec.default_channel_names();
287 break;
288 case dpx::kCbYCrA:
289 if (m_rawcolor) {
290 m_spec.channelnames.emplace_back("Cb");
291 m_spec.channelnames.emplace_back("Y");
292 m_spec.channelnames.emplace_back("Cr");
293 m_spec.channelnames.emplace_back("A");
294 m_spec.alpha_channel = 3;
295 } else {
296 m_spec.default_channel_names();
297 }
298 break;
299 default: {
300 for (int i = 0; i < m_dpx.header.ImageElementComponentCount(subimage);
301 i++) {
302 std::string ch = Strutil::sprintf("channel%d", i);
303 m_spec.channelnames.push_back(ch);
304 }
305 }
306 }
307 // bits per pixel
308 m_spec.attribute("oiio:BitsPerSample", m_dpx.header.BitDepth(subimage));
309 // image orientation - see appendix B.2 of the OIIO documentation
310 int orientation;
311 switch (m_dpx.header.ImageOrientation()) {
312 case dpx::kLeftToRightTopToBottom: orientation = 1; break;
313 case dpx::kRightToLeftTopToBottom: orientation = 2; break;
314 case dpx::kLeftToRightBottomToTop: orientation = 4; break;
315 case dpx::kRightToLeftBottomToTop: orientation = 3; break;
316 case dpx::kTopToBottomLeftToRight: orientation = 5; break;
317 case dpx::kTopToBottomRightToLeft: orientation = 6; break;
318 case dpx::kBottomToTopLeftToRight: orientation = 8; break;
319 case dpx::kBottomToTopRightToLeft: orientation = 7; break;
320 default: orientation = 0; break;
321 }
322 m_spec.attribute("Orientation", orientation);
323
324 // image linearity
325 switch (m_dpx.header.Transfer(subimage)) {
326 case dpx::kLinear: m_spec.attribute("oiio:ColorSpace", "Linear"); break;
327 case dpx::kLogarithmic:
328 m_spec.attribute("oiio:ColorSpace", "KodakLog");
329 break;
330 case dpx::kITUR709: m_spec.attribute("oiio:ColorSpace", "Rec709"); break;
331 case dpx::kUserDefined:
332 if (!std::isnan(m_dpx.header.Gamma()) && m_dpx.header.Gamma() != 0) {
333 float g = float(m_dpx.header.Gamma());
334 m_spec.attribute("oiio:ColorSpace",
335 Strutil::sprintf("GammaCorrected%.2g", g));
336 m_spec.attribute("oiio:Gamma", g);
337 break;
338 }
339 // intentional fall-through
340 /*case dpx::kPrintingDensity:
341 case dpx::kUnspecifiedVideo:
342 case dpx::kSMPTE274M:
343 case dpx::kITUR601:
344 case dpx::kITUR602:
345 case dpx::kNTSCCompositeVideo:
346 case dpx::kPALCompositeVideo:
347 case dpx::kZLinear:
348 case dpx::kZHomogeneous:
349 case dpx::kADX:
350 case dpx::kUndefinedCharacteristic:*/
351 default: break;
352 }
353 m_spec.attribute("dpx:Transfer", get_characteristic_string(
354 m_dpx.header.Transfer(subimage)));
355 // colorimetric characteristic
356 m_spec.attribute("dpx:Colorimetric",
357 get_characteristic_string(
358 m_dpx.header.Colorimetric(subimage)));
359
360 // general metadata
361 // some non-compliant writers will dump a field filled with 0xFF rather
362 // than a NULL string termination on the first character, so take that
363 // into account, too
364 if (m_dpx.header.copyright[0] && m_dpx.header.copyright[0] != (char)0xFF)
365 m_spec.attribute("Copyright", m_dpx.header.copyright);
366 if (m_dpx.header.creator[0] && m_dpx.header.creator[0] != (char)0xFF)
367 m_spec.attribute("Software", m_dpx.header.creator);
368 if (m_dpx.header.project[0] && m_dpx.header.project[0] != (char)0xFF)
369 m_spec.attribute("DocumentName", m_dpx.header.project);
370 if (m_dpx.header.creationTimeDate[0]) {
371 // libdpx's date/time format is pretty close to OIIO's (libdpx uses
372 // %Y:%m:%d:%H:%M:%S%Z)
373 char date[24];
374 Strutil::safe_strcpy(date, m_dpx.header.creationTimeDate, sizeof(date));
375 date[10] = ' ';
376 date[19] = 0;
377 m_spec.attribute("DateTime", date);
378 }
379 if (m_dpx.header.ImageEncoding(subimage) == dpx::kRLE)
380 m_spec.attribute("compression", "rle");
381 char buf[32 + 1];
382 m_dpx.header.Description(subimage, buf);
383 if (buf[0] && buf[0] != char(-1))
384 m_spec.attribute("ImageDescription", buf);
385 m_spec.attribute("PixelAspectRatio",
386 m_dpx.header.AspectRatio(1)
387 ? (m_dpx.header.AspectRatio(0)
388 / (float)m_dpx.header.AspectRatio(1))
389 : 1.0f);
390
391 // DPX-specific metadata
392 m_spec.attribute("dpx:ImageDescriptor",
393 get_descriptor_string(
394 m_dpx.header.ImageDescriptor(subimage)));
395 // save some typing by using macros
396 // "internal" macros
397 #define DPX_SET_ATTRIB_S(x, n, s) m_spec.attribute(s, m_dpx.header.x(n))
398 #define DPX_SET_ATTRIB(x, n) DPX_SET_ATTRIB_S(x, n, "dpx:" #x)
399 // set without checking for bogus attributes
400 #define DPX_SET_ATTRIB_N(x) DPX_SET_ATTRIB(x, subimage)
401 // set with checking for bogus attributes
402 #define DPX_SET_ATTRIB_BYTE(x) \
403 if (m_dpx.header.x() != 0xFF) \
404 DPX_SET_ATTRIB(x, )
405 #define DPX_SET_ATTRIB_INT_N(x) \
406 if (m_dpx.header.x(subimage) != 0xFFFFFFFF) \
407 DPX_SET_ATTRIB(x, subimage)
408 #define DPX_SET_ATTRIB_INT(x) \
409 if (m_dpx.header.x() != 0xFFFFFFFF) \
410 DPX_SET_ATTRIB(x, )
411 #define DPX_SET_ATTRIB_FLOAT_N(x) \
412 if (!std::isnan(m_dpx.header.x(subimage))) \
413 DPX_SET_ATTRIB(x, subimage)
414 #define DPX_SET_ATTRIB_FLOAT(x) \
415 if (!std::isnan(m_dpx.header.x())) \
416 DPX_SET_ATTRIB(x, )
417 // see comment above Copyright, Software and DocumentName
418 #define DPX_SET_ATTRIB_STR(X, x) \
419 if (m_dpx.header.x[0] && m_dpx.header.x[0] != char(-1)) \
420 m_spec.attribute("dpx:" #X, m_dpx.header.x)
421
422 DPX_SET_ATTRIB_INT(EncryptKey);
423 DPX_SET_ATTRIB_INT(DittoKey);
424 DPX_SET_ATTRIB_INT_N(LowData);
425 DPX_SET_ATTRIB_FLOAT_N(LowQuantity);
426 DPX_SET_ATTRIB_INT_N(HighData);
427 DPX_SET_ATTRIB_FLOAT_N(HighQuantity);
428 DPX_SET_ATTRIB_INT_N(EndOfLinePadding);
429 DPX_SET_ATTRIB_INT_N(EndOfImagePadding);
430 DPX_SET_ATTRIB_FLOAT(XScannedSize);
431 DPX_SET_ATTRIB_FLOAT(YScannedSize);
432 DPX_SET_ATTRIB_INT(FramePosition);
433 DPX_SET_ATTRIB_INT(SequenceLength);
434 DPX_SET_ATTRIB_INT(HeldCount);
435 DPX_SET_ATTRIB_FLOAT(FrameRate);
436 DPX_SET_ATTRIB_FLOAT(ShutterAngle);
437 DPX_SET_ATTRIB_STR(Version, version);
438 DPX_SET_ATTRIB_STR(Format, format);
439 DPX_SET_ATTRIB_STR(FrameId, frameId);
440 DPX_SET_ATTRIB_STR(SlateInfo, slateInfo);
441 DPX_SET_ATTRIB_STR(SourceImageFileName, sourceImageFileName);
442 DPX_SET_ATTRIB_STR(InputDevice, inputDevice);
443 DPX_SET_ATTRIB_STR(InputDeviceSerialNumber, inputDeviceSerialNumber);
444 DPX_SET_ATTRIB_BYTE(Interlace);
445 DPX_SET_ATTRIB_BYTE(FieldNumber);
446 DPX_SET_ATTRIB_FLOAT(HorizontalSampleRate);
447 DPX_SET_ATTRIB_FLOAT(VerticalSampleRate);
448 DPX_SET_ATTRIB_FLOAT(TemporalFrameRate);
449 DPX_SET_ATTRIB_FLOAT(TimeOffset);
450 DPX_SET_ATTRIB_FLOAT(BlackLevel);
451 DPX_SET_ATTRIB_FLOAT(BlackGain);
452 DPX_SET_ATTRIB_FLOAT(BreakPoint);
453 DPX_SET_ATTRIB_FLOAT(WhiteLevel);
454 DPX_SET_ATTRIB_FLOAT(IntegrationTimes);
455
456 #undef DPX_SET_ATTRIB_STR
457 #undef DPX_SET_ATTRIB_FLOAT
458 #undef DPX_SET_ATTRIB_FLOAT_N
459 #undef DPX_SET_ATTRIB_INT
460 #undef DPX_SET_ATTRIB_INT_N
461 #undef DPX_SET_ATTRIB_N
462 #undef DPX_SET_ATTRIB
463 #undef DPX_SET_ATTRIB_S
464
465 std::string tmpstr;
466 switch (m_dpx.header.ImagePacking(subimage)) {
467 case dpx::kPacked: tmpstr = "Packed"; break;
468 case dpx::kFilledMethodA: tmpstr = "Filled, method A"; break;
469 case dpx::kFilledMethodB: tmpstr = "Filled, method B"; break;
470 }
471 if (!tmpstr.empty())
472 m_spec.attribute("dpx:Packing", tmpstr);
473
474 if (m_dpx.header.filmManufacturingIdCode[0] != 0) {
475 int kc[7];
476 get_keycode_values(kc);
477 m_spec.attribute("smpte:KeyCode", TypeKeyCode, kc);
478 }
479
480 if (m_dpx.header.timeCode != 0xFFFFFFFF) {
481 unsigned int timecode[2] = { m_dpx.header.timeCode,
482 m_dpx.header.userBits };
483 m_spec.attribute("smpte:TimeCode", TypeTimeCode, timecode);
484
485 // This attribute is dpx specific and is left in for backwards compatability.
486 // Users should utilise the new smpte:TimeCode attribute instead
487 Imf::TimeCode tc(m_dpx.header.timeCode, m_dpx.header.userBits);
488 m_spec.attribute("dpx:TimeCode", get_timecode_string(tc));
489 }
490
491 // This attribute is dpx specific and is left in for backwards compatability.
492 // Users should utilise the new smpte:TimeCode attribute instead
493 if (m_dpx.header.userBits != 0xFFFFFFFF)
494 m_spec.attribute("dpx:UserBits", m_dpx.header.userBits);
495
496 if (m_dpx.header.sourceTimeDate[0]) {
497 // libdpx's date/time format is pretty close to OIIO's (libdpx uses
498 // %Y:%m:%d:%H:%M:%S%Z)
499 char date[24];
500 Strutil::safe_strcpy(date, m_dpx.header.sourceTimeDate, sizeof(date));
501 date[10] = ' ';
502 date[19] = 0;
503 m_spec.attribute("dpx:SourceDateTime", date);
504 }
505 m_dpx.header.FilmEdgeCode(buf);
506 if (buf[0])
507 m_spec.attribute("dpx:FilmEdgeCode", buf);
508
509 tmpstr.clear();
510 switch (m_dpx.header.Signal()) {
511 case dpx::kUndefined: tmpstr = "Undefined"; break;
512 case dpx::kNTSC: tmpstr = "NTSC"; break;
513 case dpx::kPAL: tmpstr = "PAL"; break;
514 case dpx::kPAL_M: tmpstr = "PAL-M"; break;
515 case dpx::kSECAM: tmpstr = "SECAM"; break;
516 case dpx::k525LineInterlace43AR:
517 tmpstr = "YCbCr ITU-R 601-5 525i, 4:3";
518 break;
519 case dpx::k625LineInterlace43AR:
520 tmpstr = "YCbCr ITU-R 601-5 625i, 4:3";
521 break;
522 case dpx::k525LineInterlace169AR:
523 tmpstr = "YCbCr ITU-R 601-5 525i, 16:9";
524 break;
525 case dpx::k625LineInterlace169AR:
526 tmpstr = "YCbCr ITU-R 601-5 625i, 16:9";
527 break;
528 case dpx::k1050LineInterlace169AR: tmpstr = "YCbCr 1050i, 16:9"; break;
529 case dpx::k1125LineInterlace169AR_274:
530 tmpstr = "YCbCr 1125i, 16:9 (SMPTE 274M)";
531 break;
532 case dpx::k1250LineInterlace169AR: tmpstr = "YCbCr 1250i, 16:9"; break;
533 case dpx::k1125LineInterlace169AR_240:
534 tmpstr = "YCbCr 1125i, 16:9 (SMPTE 240M)";
535 break;
536 case dpx::k525LineProgressive169AR: tmpstr = "YCbCr 525p, 16:9"; break;
537 case dpx::k625LineProgressive169AR: tmpstr = "YCbCr 625p, 16:9"; break;
538 case dpx::k750LineProgressive169AR:
539 tmpstr = "YCbCr 750p, 16:9 (SMPTE 296M)";
540 break;
541 case dpx::k1125LineProgressive169AR:
542 tmpstr = "YCbCr 1125p, 16:9 (SMPTE 274M)";
543 break;
544 case dpx::k255:
545 // don't set the attribute at all
546 break;
547 default:
548 tmpstr = Strutil::sprintf("Undefined %d", (int)m_dpx.header.Signal());
549 break;
550 }
551 if (!tmpstr.empty())
552 m_spec.attribute("dpx:Signal", tmpstr);
553
554 // read in user data; don't bother if the buffer is already filled (user
555 // data is per-file, not per-element)
556 if (m_userBuf.empty() && m_dpx.header.UserSize() != 0
557 && m_dpx.header.UserSize() != 0xFFFFFFFF) {
558 m_userBuf.resize(m_dpx.header.UserSize());
559 m_dpx.ReadUserData(&m_userBuf[0]);
560 }
561 if (!m_userBuf.empty())
562 m_spec.attribute("dpx:UserData",
563 TypeDesc(TypeDesc::UCHAR, m_dpx.header.UserSize()),
564 &m_userBuf[0]);
565
566 // All of the 1-channel encoding options also behave like "rawcolor",
567 // not needing color space transformations.
568 if (m_spec.nchannels == 1)
569 m_rawcolor = true;
570
571 return true;
572 }
573
574
575
576 bool
close()577 DPXInput::close()
578 {
579 if (m_io_local) {
580 // If we allocated our own ioproxy, close it.
581 m_io_local.reset();
582 m_io = nullptr;
583 }
584 // N.B. If we were passed an ioproxy from the user (m_io != nullptr, but
585 // m_io_local was not set), don't actually close it, it belongs to the
586 // caller. And in the case of an ImageCache file, it's possible that the
587 // IC won't close this ImageInput until after the owner of the IOProxy
588 // destroyed it. So don't mess with it here in close() at all, because
589 // we just can't be sure if it's still alive or not.
590
591 init(); // Reset to initial state
592 return true;
593 }
594
595
596
597 bool
read_native_scanline(int subimage,int miplevel,int y,int z,void * data)598 DPXInput::read_native_scanline(int subimage, int miplevel, int y, int z,
599 void* data)
600 {
601 return read_native_scanlines(subimage, miplevel, y, y + 1, z, data);
602 }
603
604
605
606 bool
read_native_scanlines(int subimage,int miplevel,int ybegin,int yend,int,void * data)607 DPXInput::read_native_scanlines(int subimage, int miplevel, int ybegin,
608 int yend, int /*z*/, void* data)
609 {
610 lock_guard lock(m_mutex);
611 if (!seek_subimage(subimage, miplevel))
612 return false;
613
614 dpx::Block block(0, ybegin - m_spec.y, m_dpx.header.Width() - 1,
615 yend - 1 - m_spec.y);
616
617 if (m_rawcolor) {
618 // fast path - just read the scanline in
619 if (!m_dpx.ReadBlock(subimage, (unsigned char*)data, block))
620 return false;
621 } else {
622 // read the scanline and convert to RGB
623 unsigned char* ptr = (unsigned char*)data;
624 int bufsize = dpx::QueryRGBBufferSize(m_dpx.header, subimage, block);
625 if (bufsize > 0) {
626 m_decodebuf.resize(bufsize);
627 ptr = m_decodebuf.data();
628 }
629
630 if (!m_dpx.ReadBlock(subimage, ptr, block))
631 return false;
632 if (!dpx::ConvertToRGB(m_dpx.header, subimage, ptr, data, block))
633 return false;
634 }
635
636 return true;
637 }
638
639
640
641 std::string
get_characteristic_string(dpx::Characteristic c)642 DPXInput::get_characteristic_string(dpx::Characteristic c)
643 {
644 switch (c) {
645 case dpx::kUserDefined: return "User defined";
646 case dpx::kPrintingDensity: return "Printing density";
647 case dpx::kLinear: return "Linear";
648 case dpx::kLogarithmic: return "Logarithmic";
649 case dpx::kUnspecifiedVideo: return "Unspecified video";
650 case dpx::kSMPTE274M: return "SMPTE 274M";
651 case dpx::kITUR709: return "ITU-R 709-4";
652 case dpx::kITUR601: return "ITU-R 601-5 system B or G";
653 case dpx::kITUR602: return "ITU-R 601-5 system M";
654 case dpx::kNTSCCompositeVideo: return "NTSC composite video";
655 case dpx::kPALCompositeVideo: return "PAL composite video";
656 case dpx::kZLinear: return "Z depth linear";
657 case dpx::kZHomogeneous: return "Z depth homogeneous";
658 case dpx::kADX: return "ADX";
659 case dpx::kUndefinedCharacteristic:
660 default: return "Undefined";
661 }
662 }
663
664
665
666 std::string
get_descriptor_string(dpx::Descriptor c)667 DPXInput::get_descriptor_string(dpx::Descriptor c)
668 {
669 switch (c) {
670 case dpx::kUserDefinedDescriptor:
671 case dpx::kUserDefined2Comp:
672 case dpx::kUserDefined3Comp:
673 case dpx::kUserDefined4Comp:
674 case dpx::kUserDefined5Comp:
675 case dpx::kUserDefined6Comp:
676 case dpx::kUserDefined7Comp:
677 case dpx::kUserDefined8Comp: return "User defined";
678 case dpx::kRed: return "Red";
679 case dpx::kGreen: return "Green";
680 case dpx::kBlue: return "Blue";
681 case dpx::kAlpha: return "Alpha";
682 case dpx::kLuma: return "Luma";
683 case dpx::kColorDifference: return "Color difference";
684 case dpx::kDepth: return "Depth";
685 case dpx::kCompositeVideo: return "Composite video";
686 case dpx::kRGB: return "RGB";
687 case dpx::kRGBA: return "RGBA";
688 case dpx::kABGR: return "ABGR";
689 case dpx::kCbYCrY: return "CbYCrY";
690 case dpx::kCbYACrYA: return "CbYACrYA";
691 case dpx::kCbYCr: return "CbYCr";
692 case dpx::kCbYCrA: return "CbYCrA";
693 //case dpx::kUndefinedDescriptor:
694 default: return "Undefined";
695 }
696 }
697
698
699
700 void
get_keycode_values(int * array)701 DPXInput::get_keycode_values(int* array)
702 {
703 std::stringstream ss;
704
705 // Manufacturer code
706 ss << std::string(m_dpx.header.filmManufacturingIdCode, 2);
707 ss >> array[0];
708 ss.clear();
709 ss.str("");
710
711 // Film type
712 ss << std::string(m_dpx.header.filmType, 2);
713 ss >> array[1];
714 ss.clear();
715 ss.str("");
716
717 // Prefix
718 ss << std::string(m_dpx.header.prefix, 6);
719 ss >> array[2];
720 ss.clear();
721 ss.str("");
722
723 // Count
724 ss << std::string(m_dpx.header.count, 4);
725 ss >> array[3];
726 ss.clear();
727 ss.str("");
728
729 // Perforation Offset
730 ss << std::string(m_dpx.header.perfsOffset, 2);
731 ss >> array[4];
732 ss.clear();
733 ss.str("");
734
735 // Format
736 std::string format(m_dpx.header.format, 32);
737 int& perfsPerFrame = array[5];
738 int& perfsPerCount = array[6];
739
740 // default values
741 perfsPerFrame = 4;
742 perfsPerCount = 64;
743
744 if (format == "8kimax") {
745 perfsPerFrame = 15;
746 perfsPerCount = 120;
747 } else if (format.substr(0, 4) == "2kvv" || format.substr(0, 4) == "4kvv") {
748 perfsPerFrame = 8;
749 } else if (format == "VistaVision") {
750 perfsPerFrame = 8;
751 } else if (format.substr(0, 4) == "2k35" || format.substr(0, 4) == "4k35") {
752 perfsPerFrame = 4;
753 } else if (format == "Full Aperture") {
754 perfsPerFrame = 4;
755 } else if (format == "Academy") {
756 perfsPerFrame = 4;
757 } else if (format.substr(0, 7) == "2k3perf"
758 || format.substr(0, 7) == "4k3perf") {
759 perfsPerFrame = 3;
760 } else if (format == "3perf") {
761 perfsPerFrame = 3;
762 }
763 }
764
765
766
767 std::string
get_timecode_string(Imf::TimeCode & tc)768 DPXInput::get_timecode_string(Imf::TimeCode& tc)
769 {
770 int values[] = { tc.hours(), tc.minutes(), tc.seconds(), tc.frame() };
771 std::stringstream ss;
772 for (int i = 0; i < 4; i++) {
773 std::ostringstream padded;
774 padded << std::setw(2) << std::setfill('0') << values[i];
775 ss << padded.str();
776 if (i != 3) {
777 if (i == 2) {
778 tc.dropFrame() ? ss << ';' : ss << ':';
779 } else {
780 ss << ':';
781 }
782 }
783 }
784 return ss.str();
785 }
786
787 OIIO_PLUGIN_NAMESPACE_END
788