bool DPXOutput::open (const std::string &name, const ImageSpec &userspec, OpenMode mode) { close (); // Close any already-opened file if (mode != Create) { error ("%s does not support subimages or MIP levels", format_name()); return false; } m_spec = userspec; // Stash the spec // open the image m_stream = new OutStream(); if (! m_stream->Open(name.c_str ())) { error ("Could not open file \"%s\"", name.c_str ()); return false; } // Check for things this format doesn't support if (m_spec.width < 1 || m_spec.height < 1) { error ("Image resolution must be at least 1x1, you asked for %d x %d", m_spec.width, m_spec.height); return false; } if (m_spec.depth < 1) m_spec.depth = 1; else if (m_spec.depth > 1) { error ("DPX does not support volume images (depth > 1)"); return false; } if (m_spec.format == TypeDesc::UINT8 || m_spec.format == TypeDesc::INT8) m_datasize = dpx::kByte; else if (m_spec.format == TypeDesc::UINT16 || m_spec.format == TypeDesc::INT16) m_datasize = dpx::kWord; else if (m_spec.format == TypeDesc::FLOAT || m_spec.format == TypeDesc::HALF) { m_spec.format = TypeDesc::FLOAT; m_datasize = dpx::kFloat; } else if (m_spec.format == TypeDesc::DOUBLE) m_datasize = dpx::kDouble; else { // use 16-bit unsigned integers as a failsafe m_spec.format = TypeDesc::UINT16; m_datasize = dpx::kWord; } // check if the client is giving us raw data to write m_wantRaw = m_spec.get_int_attribute ("dpx:RawData", 0) != 0; // check if the client wants endianness reverse to native // assume big endian per Jeremy's request, unless little endian is // explicitly specified std::string tmpstr = m_spec.get_string_attribute ("oiio:Endian", littleendian() ? "little" : "big"); m_wantSwap = (littleendian() != Strutil::iequals (tmpstr, "little")); m_dpx.SetOutStream (m_stream); // start out the file m_dpx.Start (); // some metadata std::string project = m_spec.get_string_attribute ("DocumentName", ""); std::string copyright = m_spec.get_string_attribute ("Copyright", ""); tmpstr = m_spec.get_string_attribute ("DateTime", ""); if (tmpstr.size () >= 19) { // libdpx's date/time format is pretty close to OIIO's (libdpx uses // %Y:%m:%d:%H:%M:%S%Z) // NOTE: the following code relies on the DateTime attribute being properly // formatted! // assume UTC for simplicity's sake, fix it if someone complains tmpstr[10] = ':'; tmpstr.replace (19, -1, "Z"); } m_dpx.SetFileInfo (name.c_str (), // filename tmpstr.c_str (), // cr. date OIIO_INTRO_STRING, // creator project.empty () ? NULL : project.c_str (), // project copyright.empty () ? NULL : copyright.c_str (), // copyright m_spec.get_int_attribute ("dpx:EncryptKey", ~0), // encryption key m_wantSwap); // image info m_dpx.SetImageInfo (m_spec.width, m_spec.height); // determine descriptor m_desc = get_descriptor_from_string (m_spec.get_string_attribute ("dpx:ImageDescriptor", "")); // transfer function dpx::Characteristic transfer; std::string colorspace = m_spec.get_string_attribute ("oiio:ColorSpace", ""); if (Strutil::iequals (colorspace, "Linear")) transfer = dpx::kLinear; else if (Strutil::iequals (colorspace, "GammaCorrected")) transfer = dpx::kUserDefined; else if (Strutil::iequals (colorspace, "Rec709")) transfer = dpx::kITUR709; else if (Strutil::iequals (colorspace, "KodakLog")) transfer = dpx::kLogarithmic; else { std::string dpxtransfer = m_spec.get_string_attribute ("dpx:Transfer", ""); transfer = get_characteristic_from_string (dpxtransfer); } // colorimetric m_cmetr = get_characteristic_from_string (m_spec.get_string_attribute ("dpx:Colorimetric", "User defined")); // select packing method dpx::Packing packing; tmpstr = m_spec.get_string_attribute ("dpx:Packing", "Filled, method A"); if (Strutil::iequals (tmpstr, "Packed")) packing = dpx::kPacked; else if (Strutil::iequals (tmpstr, "Filled, method B")) packing = dpx::kFilledMethodB; else packing = dpx::kFilledMethodA; // calculate target bit depth int bitDepth = m_spec.format.size () * 8; if (m_spec.format == TypeDesc::UINT16) { bitDepth = m_spec.get_int_attribute ("oiio:BitsPerSample", 16); if (bitDepth != 10 && bitDepth != 12 && bitDepth != 16) { error ("Unsupported bit depth %d", bitDepth); return false; } } m_dpx.header.SetBitDepth (0, bitDepth); // Bug workaround: libDPX doesn't appear to correctly support // "filled method A" for 12 bit data. Does anybody care what // packing/filling we use? Punt and just use "packed". if (bitDepth == 12) packing = dpx::kPacked; // see if we'll need to convert or not if (m_desc == dpx::kRGB || m_desc == dpx::kRGBA) { // shortcut for RGB(A) that gets the job done m_bytes = m_spec.scanline_bytes (); m_wantRaw = true; } else { m_bytes = dpx::QueryNativeBufferSize (m_desc, m_datasize, m_spec.width, 1); if (m_bytes == 0 && !m_wantRaw) { error ("Unable to deliver native format data from source data"); return false; } else if (m_bytes < 0) { // no need to allocate another buffer if (!m_wantRaw) m_bytes = m_spec.scanline_bytes (); else m_bytes = -m_bytes; } } if (m_bytes < 0) m_bytes = -m_bytes; m_dpx.SetElement (0, m_desc, bitDepth, transfer, m_cmetr, packing, dpx::kNone, (m_spec.format == TypeDesc::INT8 || m_spec.format == TypeDesc::INT16) ? 1 : 0, m_spec.get_int_attribute ("dpx:LowData", 0xFFFFFFFF), m_spec.get_float_attribute ("dpx:LowQuantity", std::numeric_limits<float>::quiet_NaN()), m_spec.get_int_attribute ("dpx:HighData", 0xFFFFFFFF), m_spec.get_float_attribute ("dpx:HighQuantity", std::numeric_limits<float>::quiet_NaN()), m_spec.get_int_attribute ("dpx:EndOfLinePadding", 0), m_spec.get_int_attribute ("dpx:EndOfImagePadding", 0)); m_dpx.header.SetXScannedSize (m_spec.get_float_attribute ("dpx:XScannedSize", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetYScannedSize (m_spec.get_float_attribute ("dpx:YScannedSize", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetFramePosition (m_spec.get_int_attribute ("dpx:FramePosition", 0xFFFFFFFF)); m_dpx.header.SetSequenceLength (m_spec.get_int_attribute ("dpx:SequenceLength", 0xFFFFFFFF)); m_dpx.header.SetHeldCount (m_spec.get_int_attribute ("dpx:HeldCount", 0xFFFFFFFF)); m_dpx.header.SetFrameRate (m_spec.get_float_attribute ("dpx:FrameRate", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetShutterAngle (m_spec.get_float_attribute ("dpx:ShutterAngle", std::numeric_limits<float>::quiet_NaN())); // FIXME: should we write the input version through or always default to 2.0? /*tmpstr = m_spec.get_string_attribute ("dpx:Version", ""); if (tmpstr.size () > 0) m_dpx.header.SetVersion (tmpstr.c_str ());*/ tmpstr = m_spec.get_string_attribute ("dpx:Format", ""); if (tmpstr.size () > 0) m_dpx.header.SetFormat (tmpstr.c_str ()); tmpstr = m_spec.get_string_attribute ("dpx:FrameId", ""); if (tmpstr.size () > 0) m_dpx.header.SetFrameId (tmpstr.c_str ()); tmpstr = m_spec.get_string_attribute ("dpx:SlateInfo", ""); if (tmpstr.size () > 0) m_dpx.header.SetSlateInfo (tmpstr.c_str ()); tmpstr = m_spec.get_string_attribute ("dpx:SourceImageFileName", ""); if (tmpstr.size () > 0) m_dpx.header.SetSourceImageFileName (tmpstr.c_str ()); tmpstr = m_spec.get_string_attribute ("dpx:InputDevice", ""); if (tmpstr.size () > 0) m_dpx.header.SetInputDevice (tmpstr.c_str ()); tmpstr = m_spec.get_string_attribute ("dpx:InputDeviceSerialNumber", ""); if (tmpstr.size () > 0) m_dpx.header.SetInputDeviceSerialNumber (tmpstr.c_str ()); m_dpx.header.SetInterlace (m_spec.get_int_attribute ("dpx:Interlace", 0xFF)); m_dpx.header.SetFieldNumber (m_spec.get_int_attribute ("dpx:FieldNumber", 0xFF)); m_dpx.header.SetHorizontalSampleRate (m_spec.get_float_attribute ("dpx:HorizontalSampleRate", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetVerticalSampleRate (m_spec.get_float_attribute ("dpx:VerticalSampleRate", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetTemporalFrameRate (m_spec.get_float_attribute ("dpx:TemporalFrameRate", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetTimeOffset (m_spec.get_float_attribute ("dpx:TimeOffset", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetBlackLevel (m_spec.get_float_attribute ("dpx:BlackLevel", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetBlackGain (m_spec.get_float_attribute ("dpx:BlackGain", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetBreakPoint (m_spec.get_float_attribute ("dpx:BreakPoint", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetWhiteLevel (m_spec.get_float_attribute ("dpx:WhiteLevel", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetIntegrationTimes (m_spec.get_float_attribute ("dpx:IntegrationTimes", std::numeric_limits<float>::quiet_NaN())); float aspect = m_spec.get_float_attribute ("PixelAspectRatio", 1.0f); int aspect_num, aspect_den; float_to_rational (aspect, aspect_num, aspect_den); m_dpx.header.SetAspectRatio (0, aspect_num); m_dpx.header.SetAspectRatio (1, aspect_den); tmpstr = m_spec.get_string_attribute ("dpx:TimeCode", ""); int tmpint = m_spec.get_int_attribute ("dpx:TimeCode", ~0); if (tmpstr.size () > 0) m_dpx.header.SetTimeCode (tmpstr.c_str ()); else if (tmpint != ~0) m_dpx.header.timeCode = tmpint; m_dpx.header.userBits = m_spec.get_int_attribute ("dpx:UserBits", ~0); tmpstr = m_spec.get_string_attribute ("dpx:SourceDateTime", ""); if (tmpstr.size () >= 19) { // libdpx's date/time format is pretty close to OIIO's (libdpx uses // %Y:%m:%d:%H:%M:%S%Z) // NOTE: the following code relies on the DateTime attribute being properly // formatted! // assume UTC for simplicity's sake, fix it if someone complains tmpstr[10] = ':'; tmpstr.replace (19, -1, "Z"); m_dpx.header.SetSourceTimeDate (tmpstr.c_str ()); } // commit! if (!m_dpx.WriteHeader ()) { error ("Failed to write DPX header"); return false; } // user data ImageIOParameter *user = m_spec.find_attribute ("dpx:UserData"); if (user && user->datasize () > 0) { if (user->datasize () > 1024 * 1024) { error ("User data block size exceeds 1 MB"); return false; } // FIXME: write the missing libdpx code /*m_dpx.SetUserData (user->datasize ()); if (!m_dpx.WriteUserData ((void *)user->data ())) { error ("Failed to write user data"); return false; }*/ } // reserve space for the image data buffer m_buf.reserve (m_bytes * m_spec.height); return true; }
bool TGAOutput::close () { if (m_file) { // write out the TGA 2.0 data fields // FIXME: write out the developer area; according to Larry, // it's probably safe to ignore it altogether until someone complains // that it's missing :) fseek (m_file, 0, SEEK_END); // write out the thumbnail, if there is one int ofs_thumb = 0; { unsigned char tw = m_spec.get_int_attribute ("thumbnail_width", 0); if (tw) { unsigned char th = m_spec.get_int_attribute ("thumbnail_width", 0); if (th) { int tc = m_spec.get_int_attribute ("thumbnail_nchannels", 0); if (tc == m_spec.nchannels) { ImageIOParameter *p = m_spec.find_attribute ("thumbnail_image"); if (p) { ofs_thumb = ftell (m_file); if (bigendian()) swap_endian (&ofs_thumb); // dump thumbnail size fwrite (&tw, 1, 1, m_file); fwrite (&th, 1, 1, m_file); // dump thumbnail data fwrite (p->data(), p->datasize(), 1, m_file); } } } } } // prepare the footer tga_footer foot = {(uint32_t)ftell (m_file), 0, "TRUEVISION-XFILE."}; if (bigendian()) { swap_endian (&foot.ofs_ext); swap_endian (&foot.ofs_dev); } // write out the extension area // ext area size short tmpint = 495; if (bigendian()) swap_endian (&tmpint); fwrite (&tmpint, sizeof (tmpint), 1, m_file); tmpint = 0; // author std::string tmpstr = m_spec.get_string_attribute ("Artist", ""); fwrite (tmpstr.c_str(), std::min (tmpstr.length (), size_t(40)), 1, m_file); // fill the rest with zeros for (int i = 41 - std::min (tmpstr.length (), size_t(40)); i > 0; i--) fwrite (&tmpint, 1, 1, m_file); // image comment tmpstr = m_spec.get_string_attribute ("ImageDescription", ""); { char *p = (char *)tmpstr.c_str (); int w = 0; // number of bytes written for (int pos = 0; w < 324 && pos < (int)tmpstr.length (); w++, pos++) { // on line breaks, fill the remainder of the line with zeros if (p[pos] == '\n') { while ((w + 1) % 81 != 0) { fwrite (&tmpint, 1, 1, m_file); w++; } continue; } fwrite (&p[pos], 1, 1, m_file); // null-terminate each line if ((w + 1) % 81 == 0) { fwrite (&tmpint, 1, 1, m_file); w++; } } // fill the rest with zeros for (; w < 324; w++) fwrite (&tmpint, 1, 1, m_file); } // timestamp tmpstr = m_spec.get_string_attribute ("DateTime", ""); { unsigned short y, m, d, h, i, s; if (tmpstr.length () > 0) sscanf (tmpstr.c_str (), "%04hu:%02hu:%02hu %02hu:%02hu:%02hu", &y, &m, &d, &h, &i, &s); else y = m = d = h = i = s = 0; if (bigendian()) { swap_endian (&y); swap_endian (&m); swap_endian (&d); swap_endian (&h); swap_endian (&i); swap_endian (&s); } fwrite (&m, sizeof (m), 1, m_file); fwrite (&d, sizeof (d), 1, m_file); fwrite (&y, sizeof (y), 1, m_file); fwrite (&h, sizeof (h), 1, m_file); fwrite (&i, sizeof (i), 1, m_file); fwrite (&s, sizeof (s), 1, m_file); } // job ID tmpstr = m_spec.get_string_attribute ("DocumentName", ""); fwrite (tmpstr.c_str(), std::min (tmpstr.length (), size_t(40)), 1, m_file); // fill the rest with zeros for (int i = 41 - std::min (tmpstr.length (), size_t(40)); i > 0; i--) fwrite (&tmpint, 1, 1, m_file); // job time tmpstr = m_spec.get_string_attribute ("targa:JobTime", ""); { unsigned short h, m, s; if (tmpstr.length () > 0) sscanf (tmpstr.c_str (), "%hu:%02hu:%02hu", &h, &m, &s); else h = m = s = 0; if (bigendian()) { swap_endian (&h); swap_endian (&m); swap_endian (&s); } fwrite (&h, sizeof (h), 1, m_file); fwrite (&m, sizeof (m), 1, m_file); fwrite (&s, sizeof (s), 1, m_file); } // software ID - we advertise ourselves tmpstr = OIIO_INTRO_STRING; fwrite (tmpstr.c_str(), std::min (tmpstr.length (), size_t(40)), 1, m_file); // fill the rest with zeros for (int i = 41 - std::min (tmpstr.length (), size_t(40)); i > 0; i--) fwrite (&tmpint, 1, 1, m_file); // software version { short v = OIIO_VERSION_MAJOR * 100 + OIIO_VERSION_MINOR * 10 + OIIO_VERSION_PATCH; if (bigendian()) swap_endian (&v); fwrite (&v, sizeof (v), 1, m_file); fwrite (&tmpint, 1, 1, m_file); } // key colour // FIXME: what do we save here? fwrite (&tmpint, 2, 1, m_file); fwrite (&tmpint, 2, 1, m_file); // pixel aspect ratio { float ratio = m_spec.get_float_attribute ("PixelAspectRatio", 1.f); float EPS = 1E-5f; if (ratio >= (0.f+EPS) && ((ratio <= (1.f-EPS))||(ratio >= (1.f+EPS)))) { // FIXME: invent a smarter way to convert to a vulgar fraction? // numerator tmpint = (unsigned short)(ratio * 10000.f); fwrite (&tmpint, 2, 1, m_file); // denominator tmpint = 10000; fwrite (&tmpint, 2, 1, m_file); // reset tmpint value tmpint = 0; } else { // just dump two zeros in there fwrite (&tmpint, 2, 1, m_file); fwrite (&tmpint, 2, 1, m_file); } } // gamma { if (iequals (m_spec.get_string_attribute ("oiio:ColorSpace"), "GammaCorrected")) { float gamma = m_spec.get_float_attribute ("oiio:Gamma", 1.0); // FIXME: invent a smarter way to convert to a vulgar fraction? // NOTE: the spec states that only 1 decimal place of precision // is needed, thus the expansion by 10 // numerator tmpint = (unsigned short)(gamma * 10.f); fwrite (&tmpint, 2, 1, m_file); // denominator tmpint = 10; fwrite (&tmpint, 2, 1, m_file); // reset tmpint value tmpint = 0; } else { // just dump two zeros in there fwrite (&tmpint, 2, 1, m_file); fwrite (&tmpint, 2, 1, m_file); } } // offset to colour correction table // FIXME: support this once it becomes clear how it's actually supposed // to be used... the spec is very unclear about this // for the time being just dump four NULL bytes fwrite (&tmpint, 2, 1, m_file); fwrite (&tmpint, 2, 1, m_file); // offset to thumbnail (endiannes has already been accounted for) fwrite (&ofs_thumb, 4, 1, m_file); // offset to scanline table // not used very widely, don't bother unless someone complains fwrite (&tmpint, 2, 1, m_file); fwrite (&tmpint, 2, 1, m_file); // alpha type { unsigned char at = (m_spec.nchannels % 2 == 0) ? TGA_ALPHA_USEFUL : TGA_ALPHA_NONE; fwrite (&at, 1, 1, m_file); } // write out the TGA footer fwrite (&foot.ofs_ext, 1, sizeof (foot.ofs_ext), m_file); fwrite (&foot.ofs_dev, 1, sizeof (foot.ofs_dev), m_file); fwrite (&foot.signature, 1, sizeof (foot.signature), m_file); // close the stream fclose (m_file); m_file = NULL; } init (); // re-initialize return true; // How can we fail? // Epicly. -- IneQuation }
bool DPXOutput::open (const std::string &name, const ImageSpec &userspec, OpenMode mode) { if (mode == Create) { m_subimage = 0; if (m_subimage_specs.size() < 1) { m_subimage_specs.resize (1); m_subimage_specs[0] = userspec; m_subimages_to_write = 1; } } else if (mode == AppendSubimage) { if (m_write_pending) write_buffer (); ++m_subimage; if (m_subimage >= m_subimages_to_write) { error ("Exceeded the pre-declared number of subimages (%d)", m_subimages_to_write); return false; } return prep_subimage (m_subimage, true); // Nothing else to do, the header taken care of when we opened with // Create. } else if (mode == AppendMIPLevel) { error ("DPX does not support MIP-maps"); return false; } // From here out, all the heavy lifting is done for Create ASSERT (mode == Create); if (is_opened()) close (); // Close any already-opened file m_stream = new OutStream(); if (! m_stream->Open(name.c_str ())) { error ("Could not open file \"%s\"", name.c_str ()); return false; } m_dpx.SetOutStream (m_stream); m_dpx.Start (); m_subimage = 0; ImageSpec &m_spec (m_subimage_specs[m_subimage]); // alias the spec // Check for things this format doesn't support if (m_spec.width < 1 || m_spec.height < 1) { error ("Image resolution must be at least 1x1, you asked for %d x %d", m_spec.width, m_spec.height); return false; } if (m_spec.depth < 1) m_spec.depth = 1; else if (m_spec.depth > 1) { error ("DPX does not support volume images (depth > 1)"); return false; } // some metadata std::string software = m_spec.get_string_attribute ("Software", ""); std::string project = m_spec.get_string_attribute ("DocumentName", ""); std::string copyright = m_spec.get_string_attribute ("Copyright", ""); std::string datestr = m_spec.get_string_attribute ("DateTime", ""); if (datestr.size () >= 19) { // libdpx's date/time format is pretty close to OIIO's (libdpx uses // %Y:%m:%d:%H:%M:%S%Z) // NOTE: the following code relies on the DateTime attribute being properly // formatted! // assume UTC for simplicity's sake, fix it if someone complains datestr[10] = ':'; datestr.replace (19, -1, "Z"); } // check if the client wants endianness reverse to native // assume big endian per Jeremy's request, unless little endian is // explicitly specified std::string endian = m_spec.get_string_attribute ("oiio:Endian", littleendian() ? "little" : "big"); m_wantSwap = (littleendian() != Strutil::iequals (endian, "little")); m_dpx.SetFileInfo (name.c_str (), // filename datestr.c_str (), // cr. date software.empty () ? OIIO_INTRO_STRING : software.c_str (), // creator project.empty () ? NULL : project.c_str (), // project copyright.empty () ? NULL : copyright.c_str (), // copyright m_spec.get_int_attribute ("dpx:EncryptKey", ~0), // encryption key m_wantSwap); // image info m_dpx.SetImageInfo (m_spec.width, m_spec.height); for (int s = 0; s < m_subimages_to_write; ++s) { prep_subimage (s, false); m_dpx.header.SetBitDepth (s, m_bitdepth); ImageSpec &spec (m_subimage_specs[s]); bool datasign = (spec.format == TypeDesc::INT8 || spec.format == TypeDesc::INT16); m_dpx.SetElement (s, m_desc, m_bitdepth, m_transfer, m_cmetr, m_packing, dpx::kNone, datasign, spec.get_int_attribute ("dpx:LowData", 0xFFFFFFFF), spec.get_float_attribute ("dpx:LowQuantity", std::numeric_limits<float>::quiet_NaN()), spec.get_int_attribute ("dpx:HighData", 0xFFFFFFFF), spec.get_float_attribute ("dpx:HighQuantity", std::numeric_limits<float>::quiet_NaN()), spec.get_int_attribute ("dpx:EndOfLinePadding", 0), spec.get_int_attribute ("dpx:EndOfImagePadding", 0)); std::string desc = spec.get_string_attribute ("ImageDescription", ""); m_dpx.header.SetDescription (s, desc.c_str()); } m_dpx.header.SetXScannedSize (m_spec.get_float_attribute ("dpx:XScannedSize", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetYScannedSize (m_spec.get_float_attribute ("dpx:YScannedSize", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetFramePosition (m_spec.get_int_attribute ("dpx:FramePosition", 0xFFFFFFFF)); m_dpx.header.SetSequenceLength (m_spec.get_int_attribute ("dpx:SequenceLength", 0xFFFFFFFF)); m_dpx.header.SetHeldCount (m_spec.get_int_attribute ("dpx:HeldCount", 0xFFFFFFFF)); m_dpx.header.SetFrameRate (m_spec.get_float_attribute ("dpx:FrameRate", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetShutterAngle (m_spec.get_float_attribute ("dpx:ShutterAngle", std::numeric_limits<float>::quiet_NaN())); // FIXME: should we write the input version through or always default to 2.0? /*tmpstr = m_spec.get_string_attribute ("dpx:Version", ""); if (tmpstr.size () > 0) m_dpx.header.SetVersion (tmpstr.c_str ());*/ std::string tmpstr; tmpstr = m_spec.get_string_attribute ("dpx:FrameId", ""); if (tmpstr.size () > 0) m_dpx.header.SetFrameId (tmpstr.c_str ()); tmpstr = m_spec.get_string_attribute ("dpx:SlateInfo", ""); if (tmpstr.size () > 0) m_dpx.header.SetSlateInfo (tmpstr.c_str ()); tmpstr = m_spec.get_string_attribute ("dpx:SourceImageFileName", ""); if (tmpstr.size () > 0) m_dpx.header.SetSourceImageFileName (tmpstr.c_str ()); tmpstr = m_spec.get_string_attribute ("dpx:InputDevice", ""); if (tmpstr.size () > 0) m_dpx.header.SetInputDevice (tmpstr.c_str ()); tmpstr = m_spec.get_string_attribute ("dpx:InputDeviceSerialNumber", ""); if (tmpstr.size () > 0) m_dpx.header.SetInputDeviceSerialNumber (tmpstr.c_str ()); m_dpx.header.SetInterlace (m_spec.get_int_attribute ("dpx:Interlace", 0xFF)); m_dpx.header.SetFieldNumber (m_spec.get_int_attribute ("dpx:FieldNumber", 0xFF)); m_dpx.header.SetHorizontalSampleRate (m_spec.get_float_attribute ("dpx:HorizontalSampleRate", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetVerticalSampleRate (m_spec.get_float_attribute ("dpx:VerticalSampleRate", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetTemporalFrameRate (m_spec.get_float_attribute ("dpx:TemporalFrameRate", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetTimeOffset (m_spec.get_float_attribute ("dpx:TimeOffset", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetBlackLevel (m_spec.get_float_attribute ("dpx:BlackLevel", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetBlackGain (m_spec.get_float_attribute ("dpx:BlackGain", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetBreakPoint (m_spec.get_float_attribute ("dpx:BreakPoint", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetWhiteLevel (m_spec.get_float_attribute ("dpx:WhiteLevel", std::numeric_limits<float>::quiet_NaN())); m_dpx.header.SetIntegrationTimes (m_spec.get_float_attribute ("dpx:IntegrationTimes", std::numeric_limits<float>::quiet_NaN())); float aspect = m_spec.get_float_attribute ("PixelAspectRatio", 1.0f); int aspect_num, aspect_den; float_to_rational (aspect, aspect_num, aspect_den); m_dpx.header.SetAspectRatio (0, aspect_num); m_dpx.header.SetAspectRatio (1, aspect_den); m_dpx.header.SetXOffset ((unsigned int)std::max (0, m_spec.x)); m_dpx.header.SetYOffset ((unsigned int)std::max (0, m_spec.y)); m_dpx.header.SetXOriginalSize ((unsigned int)m_spec.full_width); m_dpx.header.SetYOriginalSize ((unsigned int)m_spec.full_height); static int DpxOrientations[] = { 0, dpx::kLeftToRightTopToBottom, dpx::kRightToLeftTopToBottom, dpx::kLeftToRightBottomToTop, dpx::kRightToLeftBottomToTop, dpx::kTopToBottomLeftToRight, dpx::kTopToBottomRightToLeft, dpx::kBottomToTopLeftToRight, dpx::kBottomToTopRightToLeft }; int orient = m_spec.get_int_attribute ("Orientation", 0); orient = DpxOrientations[clamp (orient, 0, 8)]; m_dpx.header.SetImageOrientation ((dpx::Orientation)orient); ImageIOParameter *tc = m_spec.find_attribute("smpte:TimeCode", TypeDesc::TypeTimeCode, false); if (tc) { unsigned int *timecode = (unsigned int*) tc->data(); m_dpx.header.timeCode = timecode[0]; m_dpx.header.userBits = timecode[1]; } else { std::string timecode = m_spec.get_string_attribute ("dpx:TimeCode", ""); int tmpint = m_spec.get_int_attribute ("dpx:TimeCode", ~0); if (timecode.size () > 0) m_dpx.header.SetTimeCode (timecode.c_str ()); else if (tmpint != ~0) m_dpx.header.timeCode = tmpint; m_dpx.header.userBits = m_spec.get_int_attribute ("dpx:UserBits", ~0); } ImageIOParameter *kc = m_spec.find_attribute("smpte:KeyCode", TypeDesc::TypeKeyCode, false); if (kc) { int *array = (int*) kc->data(); set_keycode_values(array); // See if there is an overloaded dpx:Format std::string format = m_spec.get_string_attribute ("dpx:Format", ""); if (format.size () > 0) m_dpx.header.SetFormat (format.c_str ()); } std::string srcdate = m_spec.get_string_attribute ("dpx:SourceDateTime", ""); if (srcdate.size () >= 19) { // libdpx's date/time format is pretty close to OIIO's (libdpx uses // %Y:%m:%d:%H:%M:%S%Z) // NOTE: the following code relies on the DateTime attribute being properly // formatted! // assume UTC for simplicity's sake, fix it if someone complains srcdate[10] = ':'; srcdate.replace (19, -1, "Z"); m_dpx.header.SetSourceTimeDate (srcdate.c_str ()); } // set the user data size ImageIOParameter *user = m_spec.find_attribute ("dpx:UserData"); if (user && user->datasize () > 0 && user->datasize () <= 1024 * 1024) { m_dpx.SetUserData (user->datasize ()); } // commit! if (!m_dpx.WriteHeader ()) { error ("Failed to write DPX header"); return false; } // write the user data if (user && user->datasize () > 0 && user->datasize() <= 1024 * 1024) { if (!m_dpx.WriteUserData ((void *)user->data ())) { error ("Failed to write user data"); return false; } } m_dither = (m_spec.format == TypeDesc::UINT8) ? m_spec.get_int_attribute ("oiio:dither", 0) : 0; // If user asked for tiles -- which this format doesn't support, emulate // it by buffering the whole image. if (m_spec.tile_width && m_spec.tile_height) m_tilebuffer.resize (m_spec.image_bytes()); return prep_subimage (m_subimage, true); }
bool DPXOutput::open (const std::string &name, const ImageSpec &userspec, OpenMode mode) { close (); // Close any already-opened file if (mode != Create) { error ("%s does not support subimages or MIP levels", format_name()); return false; } m_spec = userspec; // Stash the spec // open the image m_stream = new OutStream(); if (! m_stream->Open(name.c_str ())) { error ("Could not open file \"%s\"", name.c_str ()); return false; } // Check for things this format doesn't support if (m_spec.width < 1 || m_spec.height < 1) { error ("Image resolution must be at least 1x1, you asked for %d x %d", m_spec.width, m_spec.height); return false; } if (m_spec.depth < 1) m_spec.depth = 1; else if (m_spec.depth > 1) { error ("DPX does not support volume images (depth > 1)"); return false; } if (m_spec.format == TypeDesc::UINT8 || m_spec.format == TypeDesc::INT8) m_datasize = dpx::kByte; else if (m_spec.format == TypeDesc::UINT16 || m_spec.format == TypeDesc::INT16) m_datasize = dpx::kWord; else if (m_spec.format == TypeDesc::FLOAT || m_spec.format == TypeDesc::HALF) { m_spec.format = TypeDesc::FLOAT; m_datasize = dpx::kFloat; } else if (m_spec.format == TypeDesc::DOUBLE) m_datasize = dpx::kDouble; else { // use 16-bit unsigned integers as a failsafe m_spec.format = TypeDesc::UINT16; m_datasize = dpx::kWord; } // check if the client is giving us raw data to write m_wantRaw = m_spec.get_int_attribute ("dpx:RawData", 0) != 0; m_dpx.SetOutStream (m_stream); // start out the file m_dpx.Start (); // some metadata std::string project = m_spec.get_string_attribute ("DocumentName", ""); std::string copyright = m_spec.get_string_attribute ("Copyright", ""); m_dpx.SetFileInfo (name.c_str (), // filename NULL, // TODO: cr. date OIIO_INTRO_STRING, // creator project.empty () ? NULL : project.c_str (), // project copyright.empty () ? NULL : copyright.c_str ()); // copyright // image info m_dpx.SetImageInfo (m_spec.width, m_spec.height); // determine descriptor m_desc = get_descriptor_from_string (m_spec.get_string_attribute ("dpx:ImageDescriptor", "")); // transfer function dpx::Characteristic transfer; std::string colorspace = m_spec.get_string_attribute ("oiio:ColorSpace", ""); if (iequals (colorspace, "Linear")) transfer = dpx::kLinear; else if (iequals (colorspace, "GammaCorrected")) transfer = dpx::kUserDefined; else if (iequals (colorspace, "Rec709")) transfer = dpx::kITUR709; else if (iequals (colorspace, "KodakLog")) transfer = dpx::kLogarithmic; else { std::string dpxtransfer = m_spec.get_string_attribute ("dpx:Transfer", ""); transfer = get_characteristic_from_string (dpxtransfer); } // colorimetric m_cmetr = get_characteristic_from_string (m_spec.get_string_attribute ("dpx:Colorimetric", "User defined")); // select packing method dpx::Packing packing; std::string tmpstr = m_spec.get_string_attribute ("dpx:ImagePacking", "Filled, method A"); if (iequals (tmpstr, "Packed")) packing = dpx::kPacked; else if (iequals (tmpstr, "Filled, method B")) packing = dpx::kFilledMethodB; else packing = dpx::kFilledMethodA; // calculate target bit depth int bitDepth = m_spec.get_int_attribute ("oiio:BitsPerSample", m_spec.format.size () * 8); if (bitDepth % 8 != 0 && bitDepth != 10 && bitDepth != 12) { error ("Unsupported bit depth %d", bitDepth); return false; } // see if we'll need to convert or not if (m_desc == dpx::kRGB || m_desc == dpx::kRGBA) { // shortcut for RGB(A) that gets the job done m_bytes = m_spec.scanline_bytes (); m_wantRaw = true; } else { m_bytes = dpx::QueryNativeBufferSize (m_desc, m_datasize, m_spec.width, 1); if (m_bytes == 0 && !m_wantRaw) { error ("Unable to deliver native format data from source data"); return false; } else if (m_bytes < 0) { // no need to allocate another buffer if (!m_wantRaw) m_bytes = m_spec.scanline_bytes (); else m_bytes = -m_bytes; } } if (m_bytes < 0) m_bytes = -m_bytes; m_dpx.SetElement (0, m_desc, bitDepth, transfer, m_cmetr, packing, dpx::kNone, (m_spec.format == TypeDesc::INT8 || m_spec.format == TypeDesc::INT16) ? 1 : 0); // commit! if (!m_dpx.WriteHeader ()) { error ("Failed to write DPX header"); return false; } // user data ImageIOParameter *user = m_spec.find_attribute ("dpx:UserData"); if (user && user->datasize () > 0) { if (user->datasize () > 1024 * 1024) { error ("User data block size exceeds 1 MB"); return false; } // FIXME: write the missing libdpx code /*m_dpx.SetUserData (user->datasize ()); if (!m_dpx.WriteUserData ((void *)user->data ())) { error ("Failed to write user data"); return false; }*/ } // reserve space for the image data buffer m_buf.reserve (m_bytes * m_spec.height); return true; }