inline bool RLAInput::read_header () { // Read the image header, which should have the same exact layout as // the m_rla structure (except for endianness issues). ASSERT (sizeof(m_rla) == 740 && "Bad RLA struct size"); if (! read (&m_rla)) { error ("RLA could not read the image header"); return false; } m_rla.rla_swap_endian (); // fix endianness if (m_rla.Revision != (int16_t)0xFFFE && m_rla.Revision != 0 /* for some reason, this can happen */) { error ("RLA header Revision number unrecognized: %d", m_rla.Revision); return false; // unknown file revision } if (m_rla.NumOfChannelBits == 0) m_rla.NumOfChannelBits = 8; // apparently, this can happen // Immediately following the header is the scanline offset table -- // one uint32_t for each scanline, giving absolute offsets (from the // beginning of the file) where the RLE records start for each // scanline of this subimage. m_sot.resize (std::abs (m_rla.ActiveBottom - m_rla.ActiveTop) + 1, 0); if (! read (&m_sot[0], m_sot.size())) { error ("RLA could not read the scanline offset table"); return false; } return true; }
bool RLAOutput::open (const std::string &name, const ImageSpec &userspec, OpenMode mode) { if (mode != Create) { error ("%s does not support subimages or MIP levels", format_name()); return false; // FIXME -- the RLA format supports subimages, but our writer // doesn't. I'm not sure if it's worth worrying about for an // old format that is so rarely used. We'll come back to it if // anybody actually encounters a multi-subimage RLA in the wild. } close (); // Close any already-opened file m_spec = userspec; // Stash the spec m_file = fopen (name.c_str(), "wb"); if (! m_file) { 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.width > 65535 || m_spec.height > 65535) { error ("Image resolution %d x %d too large for RLA (maxiumum 65535x65535)", m_spec.width, m_spec.height); return false; } if (m_spec.depth < 1) m_spec.depth = 1; else if (m_spec.depth > 1) { error ("%s does not support volume images (depth > 1)", format_name()); return false; } // prepare and write the RLA header memset (&m_rla, 0, sizeof (m_rla)); // frame and window coordinates m_rla.WindowLeft = m_spec.full_x; m_rla.WindowRight = m_spec.full_x + m_spec.full_width - 1; m_rla.WindowBottom = -m_spec.full_y; m_rla.WindowTop = m_spec.full_height - m_spec.full_y - 1; m_rla.ActiveLeft = m_spec.x; m_rla.ActiveRight = m_spec.x + m_spec.width - 1; m_rla.ActiveBottom = -m_spec.y; m_rla.ActiveTop = m_spec.height - m_spec.y - 1; m_rla.FrameNumber = m_spec.get_int_attribute ("rla:FrameNumber", 0); // figure out what's going on with the channels int remaining = m_spec.nchannels; if (m_spec.channelformats.size ()) { int streak; // accomodate first 3 channels of the same type as colour ones for (streak = 1; streak <= 3 && remaining > 0; ++streak, --remaining) if (m_spec.channelformats[streak] != m_spec.channelformats[0]) break; m_rla.ColorChannelType = m_spec.channelformats[0] == TypeDesc::FLOAT ? CT_FLOAT : CT_BYTE; int bits = m_spec.get_int_attribute ("oiio:BitsPerSample", 0); m_rla.NumOfChannelBits = bits ? bits : m_spec.channelformats[0].size () * 8; // limit to 3 in case the loop went further m_rla.NumOfColorChannels = std::min (streak, 3); // if we have anything left, treat it as alpha if (remaining) { for (streak = 1; remaining > 0; ++streak, --remaining) if (m_spec.channelformats[m_rla.NumOfColorChannels + streak] != m_spec.channelformats[m_rla.NumOfColorChannels]) break; m_rla.MatteChannelType = m_spec.channelformats[m_rla.NumOfColorChannels] == TypeDesc::FLOAT ? CT_FLOAT : CT_BYTE; m_rla.NumOfMatteBits = bits ? bits : m_spec.channelformats[m_rla.NumOfColorChannels].size () * 8; m_rla.NumOfMatteChannels = streak; } // and if there's something more left, put it in auxiliary if (remaining) { for (streak = 1; remaining > 0; ++streak, --remaining) if (m_spec.channelformats[m_rla.NumOfColorChannels + m_rla.NumOfMatteChannels + streak] != m_spec.channelformats[m_rla.NumOfColorChannels + m_rla.NumOfMatteChannels]) break; m_rla.MatteChannelType = m_spec.channelformats[m_rla.NumOfColorChannels + m_rla.NumOfMatteChannels] == TypeDesc::FLOAT ? CT_FLOAT : CT_BYTE; m_rla.NumOfAuxBits = m_spec.channelformats[m_rla.NumOfColorChannels + m_rla.NumOfMatteChannels].size () * 8; m_rla.NumOfAuxChannels = streak; } } else { m_rla.ColorChannelType = m_rla.MatteChannelType = m_rla.AuxChannelType = m_spec.format == TypeDesc::FLOAT ? CT_FLOAT : CT_BYTE; m_rla.NumOfChannelBits = m_rla.NumOfMatteBits = m_rla.NumOfAuxBits = m_spec.format.size () * 8; if (remaining >= 3) { // if we have at least 3 channels, treat them as colour m_rla.NumOfColorChannels = 3; remaining -= 3; } else { // otherwise let's say it's luminosity m_rla.NumOfColorChannels = 1; --remaining; } // if there's at least 1 more channel, it's alpha if (remaining-- > 0) ++m_rla.NumOfMatteChannels; // anything left is auxiliary if (remaining > 0) m_rla.NumOfAuxChannels = remaining; } m_rla.Revision = 0xFFFE; std::string s = m_spec.get_string_attribute ("oiio:ColorSpace", "Unknown"); if (Strutil::iequals(s, "Linear")) strcpy (m_rla.Gamma, "1.0"); else if (Strutil::iequals(s, "GammaCorrected")) snprintf (m_rla.Gamma, sizeof(m_rla.Gamma), "%.10f", m_spec.get_float_attribute ("oiio:Gamma", 1.f)); const ImageIOParameter *p; // default NTSC chromaticities p = m_spec.find_attribute ("rla:RedChroma"); set_chromaticity (p, m_rla.RedChroma, sizeof (m_rla.RedChroma), "0.67 0.08"); p = m_spec.find_attribute ("rla:GreenChroma"); set_chromaticity (p, m_rla.GreenChroma, sizeof (m_rla.GreenChroma), "0.21 0.71"); p = m_spec.find_attribute ("rla:BlueChroma"); set_chromaticity (p, m_rla.BlueChroma, sizeof (m_rla.BlueChroma), "0.14 0.33"); p = m_spec.find_attribute ("rla:WhitePoint"); set_chromaticity (p, m_rla.WhitePoint, sizeof (m_rla.WhitePoint), "0.31 0.316"); #define STRING_FIELD(rlafield,name) \ { \ std::string s = m_spec.get_string_attribute (name); \ if (s.length()) { \ strncpy (m_rla.rlafield, s.c_str(), sizeof(m_rla.rlafield));\ m_rla.rlafield[sizeof(m_rla.rlafield)-1] = 0; \ } else { \ m_rla.rlafield[0] = 0; \ } \ } m_rla.JobNumber = m_spec.get_int_attribute ("rla:JobNumber", 0); STRING_FIELD (FileName, "rla:FileName"); STRING_FIELD (Description, "ImageDescription"); STRING_FIELD (ProgramName, "Software"); STRING_FIELD (MachineName, "HostComputer"); STRING_FIELD (UserName, "Artist"); // the month number will be replaced with the 3-letter abbreviation time_t t = time (NULL); strftime (m_rla.DateCreated, sizeof (m_rla.DateCreated), "%m %d %H:%M %Y", localtime (&t)); // nice little trick - atoi() will convert the month number to integer, // which we then use to index this array of constants, and copy the // abbreviation back into the date string static const char months[12][4] = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; memcpy(m_rla.DateCreated, months[atoi (m_rla.DateCreated) - 1], 3); // FIXME: it appears that Wavefront have defined a set of aspect names; // I think it's safe not to care until someone complains STRING_FIELD (Aspect, "rla:Aspect"); snprintf (m_rla.AspectRatio, sizeof(m_rla.AspectRatio), "%.10f", m_spec.get_float_attribute ("PixelAspectRatio", 1.f)); strcpy (m_rla.ColorChannel, m_spec.get_string_attribute ("rla:ColorChannel", "rgb").c_str ()); m_rla.FieldRendered = m_spec.get_int_attribute ("rla:FieldRendered", 0); STRING_FIELD (Time, "rla:Time"); STRING_FIELD (Filter, "rla:Filter"); STRING_FIELD (AuxData, "rla:AuxData"); m_rla.rla_swap_endian (); // RLAs are big-endian write (&m_rla); m_rla.rla_swap_endian (); // flip back the endianness to native // write placeholder values - not all systems may expand the file with // zeroes upon seek m_sot.resize (m_spec.height, (int32_t)0); write (&m_sot[0], m_sot.size()); return true; }