diff --git a/src/doc/builtinplugins.tex b/src/doc/builtinplugins.tex index fcc73de30e..f9e81d30de 100644 --- a/src/doc/builtinplugins.tex +++ b/src/doc/builtinplugins.tex @@ -644,8 +644,6 @@ \section{RLA} originating from Wavefront Advanced Visualizer and used primarily by software developed at Wavefront. RLA files commonly use the file extension {\cf .rla}. -\product's support of RLA is complete - files can be both read and written. - %\subsubsection*{Attributes} \vspace{.125in} @@ -664,9 +662,10 @@ \section{RLA} \qkw{rla:FieldRendered} & int & whether the image is a field-rendered (interlaced) one (\qkw{0} for false, non-zero for true). \\ \qkw{rla:FileName} & string & name under which the file was orignally saved. \\ +\qkw{ImageDescription} & string & RLA ``Description'' of the image. \\ \qkw{Software} & string & name of software used to save the image. \\ \qkw{HostComputer} & string & name of machine used to save the image. \\ -\qkw{rla:UserName} & string & logon name of user who saved the image. \\ +\qkw{Artist} & string & RLA ``UserName'': logon name of user who saved the image. \\ \qkw{rla:Aspect} & string & aspect format description string. \\ \qkw{rla:ColorChannel} & string & textual description of color channel data format (usually \qkw{rgb}). \\ @@ -690,9 +689,8 @@ \section{RLA} \subsubsection*{Limitations} \begin{itemize} -\item \product will only write 1 image to 1 file. The format specification - allows appending more images into the same file, but this is not currently - supported by the plugin. +\item \product will only write 1 image to 1 file, multiple subimages + are not supported by the writer (but are supported by the reader). \end{itemize} diff --git a/src/include/fmath.h b/src/include/fmath.h index 996691bd56..9e784ba94e 100644 --- a/src/include/fmath.h +++ b/src/include/fmath.h @@ -489,6 +489,20 @@ inline unsigned int bit_range_convert(unsigned int in) { +// non-templated version. Slow but general +inline unsigned int +bit_range_convert(unsigned int in, unsigned int FROM_BITS, unsigned int TO_BITS) +{ + unsigned int out = 0; + int shift = TO_BITS - FROM_BITS; + for (; shift > 0; shift -= FROM_BITS) + out |= in << shift; + out |= in >> -shift; + return out; +} + + + /// A DataProxy looks like an (E &), but it really holds an (I &) /// and does conversions (via convert_type) as it reads in and out. /// (I and E are for INTERNAL and EXTERNAL data types, respectively). diff --git a/src/libOpenImageIO/fmath_test.cpp b/src/libOpenImageIO/fmath_test.cpp index fc3500ff43..e0b85e74c8 100644 --- a/src/libOpenImageIO/fmath_test.cpp +++ b/src/libOpenImageIO/fmath_test.cpp @@ -64,6 +64,16 @@ void test_convert_type () +void test_bit_range_convert () +{ + OIIO_CHECK_EQUAL ((bit_range_convert<10,16>(1023)), 65535); + OIIO_CHECK_EQUAL ((bit_range_convert<2,8>(3)), 255); + OIIO_CHECK_EQUAL ((bit_range_convert<8,8>(255)), 255); + OIIO_CHECK_EQUAL ((bit_range_convert<16,10>(65535)), 1023); +} + + + int main (int argc, char *argv[]) { std::cout << "round trip convert char/float/char\n"; @@ -77,5 +87,7 @@ int main (int argc, char *argv[]) std::cout << "round trip convert unsigned short/float/unsigned short\n"; test_convert_type (); + test_bit_range_convert(); + return unit_test_failures != 0; } diff --git a/src/oiiotool/oiiotool.cpp b/src/oiiotool/oiiotool.cpp index 6227365b5e..48cbf3b418 100644 --- a/src/oiiotool/oiiotool.cpp +++ b/src/oiiotool/oiiotool.cpp @@ -179,6 +179,7 @@ adjust_output_options (ImageSpec &spec, const Oiiotool &ot) else if (ot.output_tilewidth) { spec.tile_width = ot.output_tilewidth; spec.tile_height = ot.output_tileheight; + spec.tile_depth = 1; } if (! ot.output_compression.empty()) diff --git a/src/rla.imageio/rla_pvt.h b/src/rla.imageio/rla_pvt.h index 820cb6a9c1..8760c5ac3f 100644 --- a/src/rla.imageio/rla_pvt.h +++ b/src/rla.imageio/rla_pvt.h @@ -31,66 +31,131 @@ #ifndef OPENIMAGEIO_RLA_PVT_H #define OPENIMAGEIO_RLA_PVT_H -#include "fmath.h" +/* + Brief documentation about the RLA format: + + * The file consists of multiple subimages, merely contatenated together. + Each subimage starts with a RLAHeader, and within the header is a + NextOffset field that gives the absolute offset (relative to the start + of the file) of the beginning of the next subimage, or 0 if there + is no next subimage. + + * Immediately following the header is the scanline offset table, which + is one uint32 for each scanline, giving the absolute offset for the + beginning of that scanline record. By convention, RLA scanline 0 is + displayed at the bottom of the image (the opposite of OIIO convention). + + * Each scanline consists of up to three channel groups, concatenated + together: color, then matte, then auxiliary. Each group may have a + different data type and bit depth. + + * A channel group consists of its channels (separate, non-interleaved) + concatenated together. + + * A channel is stored in an RLE record, which consists of a uint16 + given the length of encoded data, then the encoded data run. + + * The encoded data run consists of a signed "count" byte. If the + count >= 0, the next byte is the pixel value, which is be repeated + count+1 times as output. If count < 0, then the next abs(count) + bytes should be copied directly to as output. + + * For SHORT (16 bit), LONG (32 bit), or FLOAT pixel data types, + the most significant 8 bits of each pixel come first, then the + next less significant 8 bits, and so on. For example, for 16 bit + data (HL), the sequence will be H0 H1 H2 ... L0 L1 L2 ... + Therefore, the bytes will need to be re-interleaved to form + contiguous 16 or 32 bit values in the output buffer. + + * But float data is not RLE compressed, instead just dumped raw after + the RLE length. Well, at least according to old code at SPI. We + have no original RLA specification that stipulates this to be the + case. + + * RLA files are "big endian" for all 16 and 32 bit data: header fields, + offsets, and pixel data. + + */ OIIO_PLUGIN_NAMESPACE_BEGIN namespace RLA_pvt { - // type mappings - typedef char CHAR; - typedef int16_t SHORT; - typedef int32_t LONG; // always 32-bit - - // code below comes from http://www.fileformat.info/format/wavefrontrla/egff.htm - typedef struct _WavefrontHeader + // code below adapted from + // http://www.fileformat.info/format/wavefrontrla/egff.htm + struct RLAHeader { - SHORT WindowLeft; /* Left side of the full image */ - SHORT WindowRight; /* Right side of the full image */ - SHORT WindowBottom; /* Bottom of the full image */ - SHORT WindowTop; /* Top of the full image */ - SHORT ActiveLeft; /* Left side of the viewable image */ - SHORT ActiveRight; /* Right side of viewable image */ - SHORT ActiveBottom; /* Bottom of the viewable image */ - SHORT ActiveTop; /* Top of the viewable image */ - SHORT FrameNumber; /* Frame sequence number */ - SHORT ColorChannelType; /* Data format of the image channels */ - SHORT NumOfColorChannels; /* Number of color channels in image */ - SHORT NumOfMatteChannels; /* Number of matte channels in image */ - SHORT NumOfAuxChannels; /* Number of auxiliary channels in image */ - SHORT Revision; /* File format revision number */ - CHAR Gamma[16]; /* Gamma setting of image */ - CHAR RedChroma[24]; /* Red chromaticity */ - CHAR GreenChroma[24]; /* Green chromaticity */ - CHAR BlueChroma[24]; /* Blue chromaticity */ - CHAR WhitePoint[24]; /* White point chromaticity*/ - LONG JobNumber; /* Job number ID of the file */ - CHAR FileName[128]; /* Image file name */ - CHAR Description[128]; /* Description of the file contents */ - CHAR ProgramName[64]; /* Name of the program that created the file */ - CHAR MachineName[32]; /* Name of machine used to create the file */ - CHAR UserName[32]; /* Name of user who created the file */ - CHAR DateCreated[20]; /* Date the file was created */ - CHAR Aspect[24]; /* Aspect format of the image */ - CHAR AspectRatio[8]; /* Aspect ratio of the image */ - CHAR ColorChannel[32]; /* Format of color channel data */ - SHORT FieldRendered; /* Image contains field-rendered data */ - CHAR Time[12]; /* Length of time used to create the image - file */ - CHAR Filter[32]; /* Name of post-processing filter */ - SHORT NumOfChannelBits; /* Number of bits in each color channel pixel */ - SHORT MatteChannelType; /* Data format of the matte channels */ - SHORT NumOfMatteBits; /* Number of bits in each matte channel pixel */ - SHORT AuxChannelType; /* Data format of the auxiliary channels */ - SHORT NumOfAuxBits; /* Number of bits in each auxiliary channel - pixel */ - CHAR AuxData[32]; /* Auxiliary channel data description */ - CHAR Reserved[36]; /* Unused */ - LONG NextOffset; /* Location of the next image header in the - file */ - } WAVEFRONT; + int16_t WindowLeft; // Left side of the full image + int16_t WindowRight; // Right side of the full image + int16_t WindowBottom; // Bottom of the full image + int16_t WindowTop; // Top of the full image + int16_t ActiveLeft; // Left side of the viewable image + int16_t ActiveRight; // Right side of viewable image + int16_t ActiveBottom; // Bottom of the viewable image + int16_t ActiveTop; // Top of the viewable image + int16_t FrameNumber; // Frame sequence number + int16_t ColorChannelType; // Data format of the image channels + int16_t NumOfColorChannels; // Number of color channels in image + int16_t NumOfMatteChannels; // Number of matte channels in image + int16_t NumOfAuxChannels; // Number of auxiliary channels in image + int16_t Revision; // File format revision number + char Gamma[16]; // Gamma setting of image + char RedChroma[24]; // Red chromaticity + char GreenChroma[24]; // Green chromaticity + char BlueChroma[24]; // Blue chromaticity + char WhitePoint[24]; // White point chromaticity*/ + int32_t JobNumber; // Job number ID of the file + char FileName[128]; // Image file name + char Description[128]; // Description of the file contents + char ProgramName[64]; // Name of the program that created the file + char MachineName[32]; // Name of machine used to create the file + char UserName[32]; // Name of user who created the file + char DateCreated[20]; // Date the file was created + char Aspect[24]; // Aspect format of the image + char AspectRatio[8]; // Aspect ratio of the image + char ColorChannel[32]; // Format of color channel data + int16_t FieldRendered; // Image contains field-rendered data + char Time[12]; // Length of time used to create the image file + char Filter[32]; // Name of post-processing filter + int16_t NumOfChannelBits; // Number of bits in each color channel pixel + int16_t MatteChannelType; // Data format of the matte channels + int16_t NumOfMatteBits; // Number of bits in each matte channel pixel + int16_t AuxChannelType; // Data format of the auxiliary channels + int16_t NumOfAuxBits; // Number of bits in each auxiliary channel pixel + char AuxData[32]; // Auxiliary channel data description + char Reserved[36]; // Unused + int32_t NextOffset; // Location of the next image header in the file + + void rla_swap_endian () { + if (littleendian()) { + // RLAs are big-endian + swap_endian (&WindowLeft); + swap_endian (&WindowRight); + swap_endian (&WindowBottom); + swap_endian (&WindowTop); + swap_endian (&ActiveLeft); + swap_endian (&ActiveRight); + swap_endian (&ActiveBottom); + swap_endian (&ActiveTop); + swap_endian (&FrameNumber); + swap_endian (&ColorChannelType); + swap_endian (&NumOfColorChannels); + swap_endian (&NumOfMatteChannels); + swap_endian (&NumOfAuxChannels); + swap_endian (&Revision); + swap_endian (&JobNumber); + swap_endian (&FieldRendered); + swap_endian (&NumOfChannelBits); + swap_endian (&MatteChannelType); + swap_endian (&NumOfMatteBits); + swap_endian (&AuxChannelType); + swap_endian (&NumOfAuxBits); + swap_endian (&NextOffset); + } + } + }; /// format of data enum rla_channel_type { diff --git a/src/rla.imageio/rlainput.cpp b/src/rla.imageio/rlainput.cpp index 8379499410..e5ac9d878e 100644 --- a/src/rla.imageio/rlainput.cpp +++ b/src/rla.imageio/rlainput.cpp @@ -33,13 +33,13 @@ #include #include -#include "rla_pvt.h" - #include "dassert.h" #include "typedesc.h" #include "imageio.h" #include "fmath.h" +#include "rla_pvt.h" + #include using boost::algorithm::iequals; @@ -62,10 +62,10 @@ class RLAInput : public ImageInput { private: std::string m_filename; ///< Stash the filename FILE *m_file; ///< Open image handle - WAVEFRONT m_rla; ///< Wavefront RLA header + RLAHeader m_rla; ///< Wavefront RLA header std::vector m_buf; ///< Buffer the image pixels int m_subimage; ///< Current subimage index - std::vector m_sot; ///< Scanline offsets table + std::vector m_sot; ///< Scanline offsets table int m_stride; ///< Number of bytes a contig pixel takes /// Reset everything to initial state @@ -75,28 +75,77 @@ class RLAInput : public ImageInput { m_buf.clear (); } - /// Helper: read, with error detection + /// Helper: raw read, with error detection /// bool fread (void *buf, size_t itemsize, size_t nitems) { size_t n = ::fread (buf, itemsize, nitems, m_file); if (n != nitems) - error ("Read error"); + error ("Read error: read %d records but %d expected %s", + (int)n, (int)nitems, feof(m_file) ? " (hit EOF)" : ""); return n == nitems; } - + + /// Helper: read buf[0..nitems-1], swap endianness if necessary + template + bool read (T *buf, size_t nitems=1) { + if (! fread (buf, sizeof(T), nitems)) + return false; + if (littleendian() && + (is_same::value || is_same::value || + is_same::value || is_same::value)) { + swap_endian (buf, nitems); + } + return true; + } + /// Helper function: translate 3-letter month abbreviation to number. /// inline int get_month_number (const char *s); - /// Helper: read the RLA header. + /// Helper: read the RLA header and scanline offset table. /// inline bool read_header (); - /// Helper: read and decode a single colour plane. - bool decode_plane (int first_channel, short num_channels, short num_bits); + /// Helper: read and decode a single channel group consisting of + /// channels [first_channel .. first_channel+num_channels-1], which + /// all share the same number of significant bits. + bool decode_channel_group (int first_channel, short num_channels, + short num_bits, int y); + + /// Helper: decode a span of n RLE-encoded bytes from encoded[0..elen-1] + /// into buf[0],buf[stride],buf[2*stride]...buf[(n-1)*stride]. + /// Return the number of encoded bytes we ate to fill buf. + size_t decode_rle_span (unsigned char *buf, int n, int stride, + const char *encoded, size_t elen); /// Helper: determine channel TypeDesc inline TypeDesc get_channel_typedesc (short chan_type, short chan_bits); + + // debugging aid + void preview (std::ostream &out) { + ASSERT (!feof(m_file)); + long pos = ftell (m_file); + out << "@" << pos << ", next 4 bytes are "; + union { // trickery to avoid punned pointer warnings + unsigned char c[4]; + uint16_t s[2]; + uint32_t i; + } u; + read (&u.c, 4); // because it's char, it didn't swap endian + uint16_t s[2] = { u.s[0], u.s[1] }; + uint32_t i = u.i; + if (littleendian()) { + swap_endian (s, 2); + swap_endian (&i); + } + out << Strutil::format ("%d/%u %d/%u %d/%u %d/%u (%d %d) (%u)\n", + u.c[0], ((char *)u.c)[0], + u.c[1], ((char *)u.c)[1], + u.c[2], ((char *)u.c)[2], + u.c[3], ((char *)u.c)[3], + s[0], s[1], i); + fseek (m_file, pos, SEEK_SET); + } }; @@ -137,91 +186,31 @@ RLAInput::open (const std::string &name, ImageSpec &newspec) inline bool RLAInput::read_header () { - // due to struct packing, we may get a corrupt header if we just load the - // struct from file; to adress that, read every member individually - // save some typing -#define RH(memb) if (! fread (&m_rla.memb, sizeof (m_rla.memb), 1)) \ - return false - RH(WindowLeft); - RH(WindowRight); - RH(WindowBottom); - RH(WindowTop); - RH(ActiveLeft); - RH(ActiveRight); - RH(ActiveBottom); - RH(ActiveTop); - RH(FrameNumber); - RH(ColorChannelType); - RH(NumOfColorChannels); - RH(NumOfMatteChannels); - RH(NumOfAuxChannels); - RH(Revision); - RH(Gamma); - RH(RedChroma); - RH(GreenChroma); - RH(BlueChroma); - RH(WhitePoint); - RH(JobNumber); - RH(FileName); - RH(Description); - RH(ProgramName); - RH(MachineName); - RH(UserName); - RH(DateCreated); - RH(Aspect); - RH(AspectRatio); - RH(ColorChannel); - RH(FieldRendered); - RH(Time); - RH(Filter); - RH(NumOfChannelBits); - RH(MatteChannelType); - RH(NumOfMatteBits); - RH(AuxChannelType); - RH(NumOfAuxBits); - RH(AuxData); - RH(Reserved); - RH(NextOffset); -#undef RH - if (littleendian()) { - // RLAs are big-endian - swap_endian (&m_rla.WindowLeft); - swap_endian (&m_rla.WindowRight); - swap_endian (&m_rla.WindowBottom); - swap_endian (&m_rla.WindowTop); - swap_endian (&m_rla.ActiveLeft); - swap_endian (&m_rla.ActiveRight); - swap_endian (&m_rla.ActiveBottom); - swap_endian (&m_rla.ActiveTop); - swap_endian (&m_rla.FrameNumber); - swap_endian (&m_rla.ColorChannelType); - swap_endian (&m_rla.NumOfColorChannels); - swap_endian (&m_rla.NumOfMatteChannels); - swap_endian (&m_rla.NumOfAuxChannels); - swap_endian (&m_rla.Revision); - swap_endian (&m_rla.JobNumber); - swap_endian (&m_rla.FieldRendered); - swap_endian (&m_rla.NumOfChannelBits); - swap_endian (&m_rla.MatteChannelType); - swap_endian (&m_rla.NumOfMatteBits); - swap_endian (&m_rla.AuxChannelType); - swap_endian (&m_rla.NumOfAuxBits); - swap_endian (&m_rla.NextOffset); + // 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 != (SHORT)0xFFFE) + 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 - - // load the scanline offset table - m_sot.clear (); - m_sot.resize (std::abs (m_rla.ActiveBottom - m_rla.ActiveTop) + 1); - long ofs; - for (unsigned int y = 0; y < m_sot.size (); ++y) { - if (!fread (&ofs, 4, 1)) - return false; - if (littleendian ()) - swap_endian (&ofs); - m_sot[y] = ofs; + } + 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; } @@ -233,45 +222,45 @@ RLAInput::seek_subimage (int subimage, int miplevel, ImageSpec &newspec) { if (miplevel != 0 || subimage < 0) return false; - + + if (subimage == current_subimage()) + return true; // already on the right level + + // RLA images allow multiple subimages; they are simply concatenated + // together, wth image N's header field NextOffset giving the + // absolute offset of the start of image N+1. int diff = subimage - current_subimage (); - - if (diff == 0) - // don't need to do anything - return true; if (subimage - current_subimage () < 0) { - // need to rewind to the beginning + // If we are requesting an image earlier than the current one, + // reset to the first subimage. fseek (m_file, 0, SEEK_SET); - if (!read_header ()) { - error ("Corrupt RLA header"); - return false; - } + if (!read_header ()) + return false; // read_header always calls error() diff = subimage; } - // forward scrolling + // forward scrolling -- skip subimages until we're at the right place while (diff > 0 && m_rla.NextOffset != 0) { fseek (m_file, m_rla.NextOffset, SEEK_SET); - if (!read_header ()) { - error ("Corrupt RLA header"); - return false; - } + if (!read_header ()) + return false; // read_header always calls error() --diff; } - if (diff > 0 && m_rla.NextOffset == 0) - // no more subimages to read + if (diff > 0 && m_rla.NextOffset == 0) { // no more subimages to read + error ("Unknown subimage"); return false; - - // now read metadata + } + + // Now m_rla holds the header of the requested subimage. Examine it + // to fill out our ImageSpec. + if (m_rla.ColorChannelType > CT_FLOAT) { error ("Illegal color channel type: %d", m_rla.ColorChannelType); return false; } - if (m_rla.MatteChannelType > CT_FLOAT) { error ("Illegal matte channel type: %d", m_rla.MatteChannelType); return false; } - if (m_rla.AuxChannelType > CT_FLOAT) { error ("Illegal auxiliary channel type: %d", m_rla.AuxChannelType); return false; @@ -287,8 +276,11 @@ RLAInput::seek_subimage (int subimage, int miplevel, ImageSpec &newspec) TypeDesc maxtype = (maxbytes == 4) ? TypeDesc::UINT32 : (maxbytes == 2 ? TypeDesc::UINT16 : TypeDesc::UINT8); if (nchannels < 1 || nchannels > 16 || - (maxbytes != 1 && maxbytes != 2 && maxbytes != 4)) + (maxbytes != 1 && maxbytes != 2 && maxbytes != 4)) { + error ("Failed channel bytes sanity check"); return false; // failed sanity check + } + m_spec = ImageSpec (m_rla.ActiveRight - m_rla.ActiveLeft + 1, (m_rla.ActiveTop - m_rla.ActiveBottom + 1) / (m_rla.FieldRendered ? 2 : 1), // interlaced image? @@ -345,42 +337,33 @@ RLAInput::seek_subimage (int subimage, int miplevel, ImageSpec &newspec) M = get_month_number (month); if (M > 0) { // construct a date/time marker in OIIO convention - char buf[20]; - sprintf(buf, "%4d:%02d:%02d %02d:%02d:00", y, M, d, h, m); - m_spec.attribute ("DateTime", buf); + m_spec.attribute ("DateTime", + Strutil::format("%4d:%02d:%02d %02d:%02d:00", y, M, d, h, m)); } } } - if (m_rla.Description[0]) - m_spec.attribute ("ImageDescription", m_rla.Description); - // save some typing by using macros -#define RLA_SET_ATTRIB_NOCHECK(x) m_spec.attribute ("rla:"#x, m_rla.x) -#define RLA_SET_ATTRIB(x) if (m_rla.x > 0) \ - RLA_SET_ATTRIB_NOCHECK(x) -#define RLA_SET_ATTRIB_STR(x) if (m_rla.x[0]) \ - RLA_SET_ATTRIB_NOCHECK(x) - RLA_SET_ATTRIB(FrameNumber); - RLA_SET_ATTRIB(Revision); - RLA_SET_ATTRIB(JobNumber); - RLA_SET_ATTRIB(FieldRendered); - RLA_SET_ATTRIB_STR(FileName); - RLA_SET_ATTRIB_STR(MachineName); - RLA_SET_ATTRIB_STR(UserName); - RLA_SET_ATTRIB_STR(Aspect); - RLA_SET_ATTRIB_STR(ColorChannel); - RLA_SET_ATTRIB_STR(Time); - RLA_SET_ATTRIB_STR(Filter); - RLA_SET_ATTRIB_STR(AuxData); -#undef RLA_SET_ATTRIB_STR -#undef RLA_SET_ATTRIB -#undef RLA_SET_ATTRIB_NOCHECK - - if (m_rla.ProgramName[0]) - m_spec.attribute ("Software", m_rla.ProgramName); - if (m_rla.MachineName[0]) - m_spec.attribute ("HostComputer", m_rla.MachineName); +#define FIELD(x,name) if (m_rla.x > 0) \ + m_spec.attribute (name, m_rla.x) +#define STRING_FIELD(x,name) if (m_rla.x[0]) \ + m_spec.attribute (name, m_rla.x) + STRING_FIELD (Description, "ImageDescription"); + FIELD (FrameNumber, "rla:FrameNumber"); + FIELD (Revision, "rla:Revision"); + FIELD (JobNumber, "rla:JobNumber"); + FIELD (FieldRendered, "rla:FieldRendered"); + STRING_FIELD (FileName, "rla:FileName"); + STRING_FIELD (ProgramName, "Software"); + STRING_FIELD (MachineName, "HostComputer"); + STRING_FIELD (UserName, "Artist"); + STRING_FIELD (Aspect, "rla:Aspect"); + STRING_FIELD (ColorChannel, "rla:ColorChannel"); + STRING_FIELD (Time, "rla:Time"); + STRING_FIELD (Filter, "rla:Filter"); + STRING_FIELD (AuxData, "rla:AuxData"); +#undef STRING_FIELD +#undef FIELD float f[3]; // variable will be reused for chroma, thus the array f[0] = atof (m_rla.Gamma); @@ -430,6 +413,8 @@ RLAInput::seek_subimage (int subimage, int miplevel, ImageSpec &newspec) newspec = spec (); m_subimage = subimage; + // N.B. the file pointer is now immediately after the scanline + // offset table for this subimage. return true; } @@ -449,113 +434,152 @@ RLAInput::close () +size_t +RLAInput::decode_rle_span (unsigned char *buf, int n, int stride, + const char *encoded, size_t elen) +{ + size_t e = 0; + while (n > 0 && e < elen) { + char count = encoded[e++]; + if (count >= 0) { + // run count positive: value repeated count+1 times + for (int i = 0; i <= count && n; ++i, buf += stride, --n) + *buf = encoded[e]; + ++e; + } else { + // run count negative: repeat bytes literally + count = -count; // make it positive + for ( ; count && n > 0 && e < elen; --count, buf += stride, --n) + *buf = encoded[e++]; + } + } + if (n != 0) { + error ("Read error: malformed RLE record"); + return 0; + } + return e; +} + + + bool -RLAInput::decode_plane (int first_channel, short num_channels, short num_bits) +RLAInput::decode_channel_group (int first_channel, short num_channels, + short num_bits, int y) { - int chsize, offset; - bool is_float; // float channels are not RLEd - if (m_spec.channelformats.size()) { - chsize = m_spec.channelformats[first_channel].size (); - is_float = m_spec.channelformats[first_channel] == TypeDesc::FLOAT; + // Some preliminaries -- figure out various sizes and offsets + int chsize; // size of the channels in this group, in bytes + int offset; // buffer offset to first channel + int pixelsize; // spacing between pixels (in bytes) in the output + TypeDesc chantype; // data type for the channel + if (! m_spec.channelformats.size()) { + // No per-channel formats, they are all the same, so it's easy + chantype = m_spec.format; + chsize = chantype.size (); + offset = first_channel * chsize; + pixelsize = chsize * m_spec.nchannels; + } else { + // Per-channel formats differ, need to sum them up + chantype = m_spec.channelformats[first_channel]; + chsize = chantype.size (); offset = 0; + pixelsize = m_spec.pixel_bytes (true); for (int i = 0; i < first_channel; ++i) offset += m_spec.channelformats[i].size (); - } else { - chsize = m_spec.format.size (); - is_float = m_spec.format == TypeDesc::FLOAT; - offset = first_channel * chsize; } - if (is_float) { - // floats are not run-length encoded, but simply dumped, all of them - unsigned short length; // number of encoded bytes - std::vector record; - float *out; - for (int i = 0; i < num_channels; ++i) { - if (!fread (&length, 2, 1)) - return false; - if (littleendian ()) - swap_endian (&length); - record.resize (std::max (record.size (), (size_t)length / sizeof (float))); - ASSERT(length <= m_buf.size ()); - if (!fread (&record[0], 1, length)) - return false; - out = (float *)&m_buf[i * chsize + offset]; - for (std::vector::iterator it = record.begin (); - it != record.end (); - ++it, out = (float *)((unsigned char *)(out) + m_stride)) { - ASSERT((unsigned char *)out - &m_buf[0] < (int)m_buf.size ()); - *out = *it; - } + // Read the big-endian values into the buffer. + // The channels are simply contatenated together in order. + // Each channel starts with a length, from which we know how many + // bytes of encoded RLE data to read. Then there are RLE + // spans for each 8-bit slice of the channel. + std::vector encoded; + for (int c = 0; c < num_channels; ++c) { + // Read the length + uint16_t length; // number of encoded bytes + if (!read (&length)) { + error ("Read error: couldn't read RLE record length"); + return false; } - } else { - // integer values, run-length encoded - unsigned short length; // number of encoded bytes - char rc; // run count - unsigned char *p; // pointer to current byte - std::vector record; - for (int i = 0; i < num_channels * chsize; ++i) { - int x = 0; // index of pixel inside the scanline - if (!fread (&length, 2, 1)) - return false; - if (littleendian ()) - swap_endian (&length); - record.resize (std::max (record.size (), (size_t)length)); - if (!fread (&record[0], 1, length)) + // Read the encoded RLE record + encoded.resize (length); + if (!read (&encoded[0], length)) { + error ("Read error: couldn't read RLE data span"); + return false; + } + + if (chantype == TypeDesc::FLOAT) { + // Special case -- float data is just dumped raw, no RLE + for (int x = 0; x < m_spec.width; ++x) + *((float *)&m_buf[offset+c*chsize+x*pixelsize]) = + ((float *)&encoded[0])[x]; + continue; + } + + // Decode RLE -- one pass for each significant byte of the file, + // which we re-interleave properly by passing the right offsets + // and strides to decode_rle_span. + size_t eoffset = 0; + for (int bytes = 0; bytes < chsize; ++bytes) { + size_t e = decode_rle_span (&m_buf[offset+c*chsize+bytes], + m_spec.width, pixelsize, + &encoded[eoffset], length); + if (! e) return false; - p = &record[0]; - while (p - &record[0] < length) { - rc = *((char *)p++); - if (rc >= 0) { - // replicate value run count + 1 times - for (; rc >= 0; --rc, ++x) { - ASSERT(x * m_stride + i + offset < (int)m_buf.size ()); - m_buf[x * m_stride + i + offset] = *p; - } - // advance pointer by 1 datum - ++p; - } else if (rc < 0) { - // copy raw values run count times - for (; rc < 0; ++rc, ++x) { - ASSERT(x * m_stride + i + offset < (int)m_buf.size ()); - m_buf[x * m_stride + i + offset] = *p; - // advance pointer by 1 datum - ++p; - } - } - // exceeding the width while remaining inside the record means that - // the less significant byte pass begins - if (x >= m_spec.width && p - &record[0] < length) { - x = 0; - ++i; - if (i >= num_channels * chsize) - break; - } - } - // make sure we haven't gone way off range - ASSERT(p - &record[0] <= length); + eoffset += e; } } - // reverse byte order (RLA is always big-endian) and expand bit range if needed - if (chsize > 1 && littleendian () != is_float) { - for (int i = 0; i < num_channels; ++i) { - for (int x = 0; x < m_spec.width; ++x) { - switch (chsize) { - case 2: - swap_endian ((short *)&m_buf[x * m_stride - + i * chsize + offset]); - if (!is_float && num_bits == 10) - *(short *)&m_buf[x * m_stride + i * chsize + offset] = - bit_range_convert<10, 16>(*(short *)&m_buf - [x * m_stride + i * chsize + offset]); - break; - case 4: - swap_endian ((int *)&m_buf[x * m_stride - + i * chsize + offset]); - break; - default: ASSERT (!"Invalid channel size!"); - } - } + + // If we're little endian, swap endianness in place for 2- and + // 4-byte pixel data. + if (littleendian()) { + if (chsize == 2) { + if (num_channels == m_spec.nchannels) + swap_endian ((uint16_t *)&m_buf[0], num_channels*m_spec.width); + else + for (int x = 0; x < m_spec.width; ++x) + swap_endian ((uint16_t *)&m_buf[offset+x*pixelsize], num_channels); + } else if (chsize == 4 && chantype != TypeDesc::FLOAT) { + if (num_channels == m_spec.nchannels) + swap_endian ((uint32_t *)&m_buf[0], num_channels*m_spec.width); + else + for (int x = 0; x < m_spec.width; ++x) + swap_endian ((uint32_t *)&m_buf[offset+x*pixelsize], num_channels); + } + } + + // If not 8*2^n bits, need to rescale. For example, if num_bits is + // 10, the data values run 0-1023, but are stored in uint16. So we + // now rescale to the full range of the output buffer range, per + // OIIO conventions. + if (num_bits == 8 || num_bits == 16 || num_bits == 32) { + // ok -- no rescaling needed + } else if (num_bits == 10) { + // fast, common case -- use templated hard-code + for (int x = 0; x < m_spec.width; ++x) { + uint16_t *b = (uint16_t *)(&m_buf[offset+x*pixelsize]); + for (int c = 0; c < num_channels; ++c) + b[c] = bit_range_convert<10,16> (b[c]); + } + } else if (num_bits < 8) { + // rare case, use slow code to make this clause short and simple + for (int x = 0; x < m_spec.width; ++x) { + uint8_t *b = (uint8_t *)&m_buf[offset+x*pixelsize]; + for (int c = 0; c < num_channels; ++c) + b[c] = bit_range_convert (b[c], num_bits, 8); + } + } else if (num_bits > 8 && num_bits < 16) { + // rare case, use slow code to make this clause short and simple + for (int x = 0; x < m_spec.width; ++x) { + uint16_t *b = (uint16_t *)&m_buf[offset+x*pixelsize]; + for (int c = 0; c < num_channels; ++c) + b[c] = bit_range_convert (b[c], num_bits, 16); + } + } else if (num_bits > 16 && num_bits < 32) { + // rare case, use slow code to make this clause short and simple + for (int x = 0; x < m_spec.width; ++x) { + uint32_t *b = (uint32_t *)&m_buf[offset+x*pixelsize]; + for (int c = 0; c < num_channels; ++c) + b[c] = bit_range_convert (b[c], num_bits, 32); } } return true; @@ -566,26 +590,33 @@ RLAInput::decode_plane (int first_channel, short num_channels, short num_bits) bool RLAInput::read_native_scanline (int y, int z, void *data) { + // By convention, RLA images store their images bottom-to-top. y = m_spec.height - y - 1; - m_buf.resize (m_spec.scanline_bytes (true)); - - // seek to scanline start + + // Seek to scanline start, based on the scanline offset table fseek (m_file, m_sot[y], SEEK_SET); - - // now decode and interleave the planes + + // Now decode and interleave the channels. + // The channels are non-interleaved (i.e. rrrrrgggggbbbbb...). + // Color first, then matte, then auxiliary channels. We can't + // decode all in one shot, though, because the data type and number + // of significant bits may be may be different for each class of + // channels, so we deal with them separately and interleave into + // our buffer as we go. + size_t size = m_spec.scanline_bytes(true); + m_buf.resize (size); if (m_rla.NumOfColorChannels > 0) - if (!decode_plane(0, m_rla.NumOfColorChannels, m_rla.NumOfChannelBits)) + if (!decode_channel_group(0, m_rla.NumOfColorChannels, m_rla.NumOfChannelBits, y)) return false; if (m_rla.NumOfMatteChannels > 0) - if (!decode_plane(m_rla.NumOfColorChannels, m_rla.NumOfMatteChannels, - m_rla.NumOfMatteBits)) + if (!decode_channel_group(m_rla.NumOfColorChannels, m_rla.NumOfMatteChannels, + m_rla.NumOfMatteBits, y)) return false; if (m_rla.NumOfAuxChannels > 0) - if (!decode_plane(m_rla.NumOfColorChannels + m_rla.NumOfMatteChannels, - m_rla.NumOfAuxChannels, m_rla.NumOfAuxBits)) + if (!decode_channel_group(m_rla.NumOfColorChannels + m_rla.NumOfMatteChannels, + m_rla.NumOfAuxChannels, m_rla.NumOfAuxBits, y)) return false; - size_t size = spec().scanline_bytes(true); memcpy (data, &m_buf[0], size); return true; } @@ -595,30 +626,13 @@ RLAInput::read_native_scanline (int y, int z, void *data) inline int RLAInput::get_month_number (const char *s) { - if (iequals (s, "jan")) - return 1; - if (iequals (s, "feb")) - return 2; - if (iequals (s, "mar")) - return 3; - if (iequals (s, "apr")) - return 4; - if (iequals (s, "may")) - return 5; - if (iequals (s, "jun")) - return 6; - if (iequals (s, "jul")) - return 7; - if (iequals (s, "aug")) - return 8; - if (iequals (s, "sep")) - return 9; - if (iequals (s, "oct")) - return 10; - if (iequals (s, "nov")) - return 11; - if (iequals (s, "dec")) - return 12; + static const char *months[] = { + "", "jan", "feb", "mar", "apr", "may", "jun", + "jul", "aug", "sep", "oct", "nov", "dec" + }; + for (int i = 1; i <= 12; ++i) + if (iequals (s, months[i])) + return i; return -1; } diff --git a/src/rla.imageio/rlaoutput.cpp b/src/rla.imageio/rlaoutput.cpp index 7335b1f757..7864b66ca3 100644 --- a/src/rla.imageio/rlaoutput.cpp +++ b/src/rla.imageio/rlaoutput.cpp @@ -35,12 +35,13 @@ #include using boost::algorithm::iequals; -#include "rla_pvt.h" - #include "dassert.h" #include "typedesc.h" #include "imageio.h" #include "fmath.h" +#include "sysutil.h" + +#include "rla_pvt.h" #ifdef WIN32 # define snprintf _snprintf @@ -68,9 +69,9 @@ class RLAOutput : public ImageOutput { std::string m_filename; ///< Stash the filename FILE *m_file; ///< Open image handle std::vector m_scratch; - WAVEFRONT m_rla; ///< Wavefront RLA header - std::vector m_sot; ///< Scanline offset table - std::vector m_buf; ///< Run record buffer for RLE + RLAHeader m_rla; ///< Wavefront RLA header + std::vector m_sot; ///< Scanline offset table + std::vector m_rle; ///< Run record buffer for RLE // Initialize private members to pre-opened state void init (void) { @@ -83,17 +84,31 @@ class RLAOutput : public ImageOutput { size_t field_size, const char *default_val); /// Helper - handles the repetitive work of encoding and writing a channel - bool encode_plane (const unsigned char *data, stride_t xstride, - int chsize, bool is_float); + bool encode_channel (const unsigned char *data, stride_t xstride, + TypeDesc chantype, int bits); - /// Helper - flushes the current RLE run into the record buffer - inline void flush_run (int& rawcount, int& rlecount, - std::vector::iterator& it); - - /// Helper - flushes the current RLE record into file - inline bool flush_record (int& rawcount, int& rlecount, - unsigned short& length, - std::vector::iterator& it); + /// Helper - write, with error detection + bool fwrite (const void *buf, size_t itemsize, size_t nitems) { + size_t n = ::fwrite (buf, itemsize, nitems, m_file); + if (n != nitems) + error ("Write error: wrote %d records of %d", (int)n, (int)nitems); + return n == nitems; + } + + /// Helper: write buf[0..nitems-1], swap endianness if necessary + template + bool write (const T *buf, size_t nitems=1) { + if (littleendian() && + (is_same::value || is_same::value || + is_same::value || is_same::value)) { + T *newbuf = ALLOCA(T,nitems); + memcpy (newbuf, buf, nitems*sizeof(T)); + swap_endian (newbuf, nitems); + buf = newbuf; + } + return fwrite (buf, sizeof(T), nitems); + } + }; @@ -146,6 +161,10 @@ RLAOutput::open (const std::string &name, const ImageSpec &userspec, 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 @@ -163,6 +182,11 @@ RLAOutput::open (const std::string &name, const ImageSpec &userspec, 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; @@ -196,7 +220,8 @@ RLAOutput::open (const std::string &name, const ImageSpec &userspec, break; m_rla.ColorChannelType = m_spec.channelformats[0] == TypeDesc::FLOAT ? CT_FLOAT : CT_BYTE; - m_rla.NumOfChannelBits = m_spec.channelformats[0].size () * 8; + 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 @@ -207,7 +232,7 @@ RLAOutput::open (const std::string &name, const ImageSpec &userspec, break; m_rla.MatteChannelType = m_spec.channelformats[m_rla.NumOfColorChannels] == TypeDesc::FLOAT ? CT_FLOAT : CT_BYTE; - m_rla.NumOfMatteBits = m_spec.channelformats[m_rla.NumOfColorChannels].size () * 8; + 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 @@ -267,22 +292,23 @@ RLAOutput::open (const std::string &name, const ImageSpec &userspec, 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); - strncpy (m_rla.FileName, name.c_str (), sizeof (m_rla.FileName)); - - s = m_spec.get_string_attribute ("ImageDescription", ""); - if (s.length ()) - strncpy (m_rla.Description, s.c_str (), sizeof (m_rla.Description)); - - // yay for advertising! - strcpy (m_rla.ProgramName, OIIO_INTRO_STRING); - - s = m_spec.get_string_attribute ("HostComputer", ""); - if (s.length ()) - strncpy (m_rla.MachineName, s.c_str (), sizeof (m_rla.MachineName)); - s = m_spec.get_string_attribute ("rla:UserName", ""); - if (s.length ()) - strncpy (m_rla.UserName, s.c_str (), sizeof (m_rla.UserName)); + 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); @@ -309,119 +335,27 @@ RLAOutput::open (const std::string &name, const ImageSpec &userspec, // FIXME: it appears that Wavefront have defined a set of aspect names; // I think it's safe not to care until someone complains - s = m_spec.get_string_attribute ("rla:Aspect", ""); - if (s.length ()) - strncpy (m_rla.Aspect, s.c_str (), sizeof (m_rla.Aspect)); + 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); - - s = m_spec.get_string_attribute ("rla:Time", ""); - if (s.length ()) - strncpy (m_rla.Time, s.c_str (), sizeof (m_rla.Time)); - - s = m_spec.get_string_attribute ("rla:Filter", ""); - if (s.length ()) - strncpy (m_rla.Filter, s.c_str (), sizeof (m_rla.Filter)); - - s = m_spec.get_string_attribute ("rla:AuxData", ""); - if (s.length ()) - strncpy (m_rla.AuxData, s.c_str (), sizeof (m_rla.AuxData)); - - if (littleendian()) { - // RLAs are big-endian - swap_endian (&m_rla.WindowLeft); - swap_endian (&m_rla.WindowRight); - swap_endian (&m_rla.WindowBottom); - swap_endian (&m_rla.WindowTop); - swap_endian (&m_rla.ActiveLeft); - swap_endian (&m_rla.ActiveRight); - swap_endian (&m_rla.ActiveBottom); - swap_endian (&m_rla.ActiveTop); - swap_endian (&m_rla.FrameNumber); - swap_endian (&m_rla.ColorChannelType); - swap_endian (&m_rla.NumOfColorChannels); - swap_endian (&m_rla.NumOfMatteChannels); - swap_endian (&m_rla.NumOfAuxChannels); - swap_endian (&m_rla.Revision); - swap_endian (&m_rla.JobNumber); - swap_endian (&m_rla.FieldRendered); - swap_endian (&m_rla.NumOfChannelBits); - swap_endian (&m_rla.MatteChannelType); - swap_endian (&m_rla.NumOfMatteBits); - swap_endian (&m_rla.AuxChannelType); - swap_endian (&m_rla.NumOfAuxBits); - swap_endian (&m_rla.NextOffset); - } - // due to struct packing, we may get a corrupt header if we just dump the - // struct to the file; to adress that, write every member individually - // save some typing -#define WH(memb) fwrite (&m_rla.memb, sizeof (m_rla.memb), 1, m_file) - WH(WindowLeft); - WH(WindowRight); - WH(WindowBottom); - WH(WindowTop); - WH(ActiveLeft); - WH(ActiveRight); - WH(ActiveBottom); - WH(ActiveTop); - WH(FrameNumber); - WH(ColorChannelType); - WH(NumOfColorChannels); - WH(NumOfMatteChannels); - WH(NumOfAuxChannels); - WH(Revision); - WH(Gamma); - WH(RedChroma); - WH(GreenChroma); - WH(BlueChroma); - WH(WhitePoint); - WH(JobNumber); - WH(FileName); - WH(Description); - WH(ProgramName); - WH(MachineName); - WH(UserName); - WH(DateCreated); - WH(Aspect); - WH(AspectRatio); - WH(ColorChannel); - WH(FieldRendered); - WH(Time); - WH(Filter); - WH(NumOfChannelBits); - WH(MatteChannelType); - WH(NumOfMatteBits); - WH(AuxChannelType); - WH(NumOfAuxBits); - WH(AuxData); - WH(Reserved); - WH(NextOffset); -#undef WH + + 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); - fwrite (&m_sot[0], sizeof(int32_t), m_sot.size (), m_file); - - // flip back the endianness of some things we will need again - if (littleendian ()) { - swap_endian (&m_rla.NumOfColorChannels); - swap_endian (&m_rla.NumOfMatteChannels); - swap_endian (&m_rla.NumOfAuxChannels); - swap_endian (&m_rla.NumOfChannelBits); - swap_endian (&m_rla.NumOfMatteBits); - swap_endian (&m_rla.NumOfAuxBits); - } + write (&m_sot[0], m_sot.size()); - // resize run record buffer to accomodate a worst-case scenario - // 2 bytes for record length, 1 byte per 1 longest run - m_buf.resize (2 + (size_t)ceil((1.0 + 1.0 / 128.0) - * m_spec.scanline_bytes(true))); - return true; } @@ -453,17 +387,16 @@ bool RLAOutput::close () { if (m_file) { - // dump the scanline offset table to file - fseek (m_file, 740, SEEK_SET); - fwrite (&m_sot[0], sizeof(int32_t), m_sot.size (), m_file); + // Now that all scanlines ahve been output, return to write the + // correct scanline offset table to file. + fseek (m_file, sizeof(RLAHeader), SEEK_SET); + write (&m_sot[0], m_sot.size()); // close the stream fclose (m_file); m_file = NULL; } - m_buf.clear (); - init (); // re-initialize return true; // How can we fail? // Epicly. -- IneQuation @@ -471,172 +404,103 @@ RLAOutput::close () -inline void -RLAOutput::flush_run (int& rawcount, int& rlecount, - std::vector::iterator& it) -{ - if (rawcount > 0) { - // take advantage of two's complement arithmetic - *(it - rawcount - 1) = ~((unsigned char)rawcount) + 1; - rawcount = 0; - } else if (rlecount > 0) { - *(it - 2) = (unsigned char)(rlecount - 1); - rlecount = 0; - } -} - - - -inline bool -RLAOutput::flush_record (int& rawcount, int& rlecount, unsigned short& length, - std::vector::iterator& it) -{ - flush_run (rawcount, rlecount, it); - it = m_buf.begin (); - if (littleendian ()) { - *it++ = ((unsigned char *)&length)[1]; - *it++ = ((unsigned char *)&length)[0]; - } else { - *it++ = ((unsigned char *)&length)[0]; - *it++ = ((unsigned char *)&length)[1]; - } - length += 2; // account for the record length - if (fwrite (&m_buf[0], 1, length, m_file) != length) - return false; - // reserve 2 bytes for the record length - it = m_buf.begin () + 2; - length = 0; - return true; -} - - - bool -RLAOutput::encode_plane (const unsigned char *data, stride_t xstride, - int chsize, bool is_float) +RLAOutput::encode_channel (const unsigned char *data, stride_t xstride, + TypeDesc chantype, int bits) { - unsigned short length; - if (is_float) { - // fast path for floats - just dump the plane into the file - float f; - length = m_spec.width * sizeof(float); - if (littleendian ()) - swap_endian (&length); - fwrite (&length, sizeof (length), 1, m_file); - for (int x = 0; x < m_spec.width; ++x) { - f = *((float *)(data + x * xstride)); - if (bigendian ()) - swap_endian (&length); - fwrite (&f, sizeof (float), 1, m_file); - } + if (chantype == TypeDesc::FLOAT) { + // Special case -- float data is just dumped raw, no RLE + uint16_t size = m_spec.width * sizeof(float); + write (&size); + for (int x = 0; x < m_spec.width; ++x) + write ((const float *)&data[x*xstride]); return true; } - // integer values - RLE - unsigned char first = 0; - const unsigned char *d = 0; - int rawcount = 0, rlecount = 0; - length = 0; - // keeps track of the record buffer position - // reserve 2 bytes for the record length - std::vector::iterator it = m_buf.begin () + 2; - for (int i = 0; i < chsize; ++i) { - // ensure correct byte order - if (littleendian ()) - i = chsize - i - 1; - for (int x = 0; x < m_spec.width; ++x) { - ASSERT (it < m_buf.end ()); - d = data + x * xstride + i; - bool contig = first == *d; - - // if the record has ended, flush it run and start anew - if (length == (1 << (sizeof (length) * 8)) - 1 - && !flush_record (rawcount, rlecount, length, it)) - return false; - - if (rawcount == 0 && rlecount == 0) { - // start of record - ++it; // run length placeholder - *it++ = first = *d; - rlecount = 1; - length += 2; - } else if (rawcount > 0) { - // raw packet - if (rawcount == 128) { - // max run length reached, flush - flush_run (rawcount, rlecount, it); - // start a new RLE run - ++it; // run length placeholder - *it++ = first = *d; - rlecount = 1; - length += 2; + + m_rle.resize (2); // reserve t bytes for the encoded size + + // multi-byte data types are sliced to MSB, nextSB, ..., LSB + int chsize = (int)chantype.size(); + for (int byte = 0; byte < chsize; ++byte) { + int lastval = -1; // last value + int count = 0; // count of raw or repeats + bool repeat = false; // if true, we're repeating + int runbegin = 0; // where did the run begin + int byteoffset = bigendian() ? byte : (chsize-byte-1); + for (int x = 0; x < m_spec.width; ++x) { + int newval = data[x*xstride+byteoffset]; + if (count == 0) { // beginning of a run. + count = 1; + repeat = true; // presumptive + runbegin = x; + } else if (repeat) { // We've seen one or more repeating characters + if (newval == lastval) { + // another repeating value + ++count; + } else { + // We stopped repeating. + if (count < 3) { + // If we didn't even have 3 in a row, just + // retroactively treat it as a raw run. + ++count; + repeat = false; + } else { + // We are ending a 3+ repetition + m_rle.push_back (count-1); + m_rle.push_back (lastval); + count = 1; + runbegin = x; + } + } + } else { // Have not been repeating + if (newval == lastval) { + // starting a repetition? Output previous + ASSERT (count > 1); + // write everything but the last char + --count; + m_rle.push_back (-count); + for (int i = 0; i < count; ++i) + m_rle.push_back (data[(runbegin+i)*xstride+byteoffset]); + count = 2; + runbegin = x - 1; + repeat = true; } else { - ++rawcount; - if (contig) { - if (rlecount == 0) - rlecount = 2; // we wouldn't have noticed the 1st one - else - ++rlecount; - // we have 3 contiguous bytes, flush and start RLE - if (rlecount >= 3) { - // rewind the iterator and the counters - it -= rlecount - 1; - rawcount -= rlecount; - length -= rlecount - 2; // will be incremented below - // flush the raw run - flush_run (rawcount, rlecount, it); - // start the RLE run - // will be incremented below - ++it; - } - } else - // reset contiguity counter - rlecount = 0; - // also reset the comparison base - *it++ = first = *d; - ++length; + ++count; // another non-repeat } - } else { - // RLE packet - if (rlecount == 128 || (!contig && rlecount >= 3)) { - // run ended, flush - flush_run (rawcount, rlecount, it); - // start a new RLE run - ++it; // run length placeholder - *it++ = first = *d; - rlecount = 1; - length += 2; - } else if (contig) - // another same byte - ++rlecount; - else { - // not contiguous, turn the remainder into a raw run - for (int j = 1; j < rlecount; ++j, ++length) - *it++ = first; - rawcount = rlecount + 1; - rlecount = 0; - // also reset the comparison base - *it++ = first = *d; - ++length; + } + + // If the run is too long or we're at the scanline end, write + if (count == 127 || x == m_spec.width-1) { + if (repeat) { + m_rle.push_back (count-1); + m_rle.push_back (lastval); + } else { + m_rle.push_back (-count); + for (int i = 0; i < count; ++i) + m_rle.push_back (data[(runbegin+i)*xstride+byteoffset]); } + count = 0; } + lastval = newval; } - // if there's anything left in the run, flush it - flush_run (rawcount, rlecount, it); - // restore index for proper loop functioning - if (littleendian ()) - i = chsize - i - 1; + ASSERT (count == 0); } - // if there's anything left in the buffer, flush it - if (length > 0) - return flush_record (rawcount, rlecount, length, it); - return true; + + // Now that we know the size of the encoded buffer, save it at the + // beginning + uint16_t size = uint16_t (m_rle.size() - 2); + m_rle[0] = size >> 8; + m_rle[1] = size & 255; + + // And write the channel to the file + return write (&m_rle[0], m_rle.size()); } bool RLAOutput::write_scanline (int y, int z, TypeDesc format, - const void *data, stride_t xstride) + const void *data, stride_t xstride) { m_spec.auto_stride (xstride, format, spec().nchannels); const void *origdata = data; @@ -647,46 +511,22 @@ RLAOutput::write_scanline (int y, int z, TypeDesc format, data = &m_scratch[0]; } - // store the offset to the scanline - m_sot[m_spec.height - y - 1] = (int32_t)ftell (m_file); - if (littleendian ()) - swap_endian (&m_sot[m_spec.height - y - 1]); - - bool allsame = !m_spec.channelformats.size (); - bool is_float = (allsame ? m_spec.format : m_spec.channelformats[0]) - == TypeDesc::FLOAT; + // store the offset to the scanline. We'll swap_endian if necessary + // when we go to actually write it. + m_sot[m_spec.height - y - 1] = (uint32_t)ftell (m_file); + + size_t pixelsize = m_spec.pixel_bytes (true /*native*/); int offset = 0; - // colour channels - int chsize = allsame ? m_spec.format.size () - : m_spec.channelformats[0].size (); - for (int i = 0; i < m_rla.NumOfColorChannels; ++i, offset += chsize) { - if (!encode_plane ((unsigned char *)data + offset, xstride, chsize, - is_float)) - return false; - } - // alpha (matte) channels - is_float = (allsame ? m_spec.format - : m_spec.channelformats[m_rla.NumOfColorChannels]) - == TypeDesc::FLOAT; - chsize = allsame ? m_spec.format.size () - : m_spec.channelformats[m_rla.NumOfColorChannels].size (); - for (int i = 0; i < m_rla.NumOfMatteChannels; ++i, offset += chsize) { - if (!encode_plane ((unsigned char *)data + offset, xstride, chsize, - is_float)) - return false; - } - // aux (depth) channels - is_float = (allsame ? m_spec.format - : m_spec.channelformats[m_rla.NumOfColorChannels - + m_rla.NumOfMatteChannels]) - == TypeDesc::FLOAT; - chsize = allsame ? m_spec.format.size () - : m_spec.channelformats[m_rla.NumOfColorChannels - + m_rla.NumOfMatteChannels].size (); - for (int i = 0; i < m_rla.NumOfAuxChannels; ++i, offset += chsize) { - if (!encode_plane ((unsigned char *)data + offset, xstride, chsize, - is_float)) + for (int c = 0; c < m_spec.nchannels; ++c) { + TypeDesc chantype = m_spec.channelformats.size() ? + m_spec.channelformats[c] : m_spec.format; + int bits = (c < m_rla.NumOfColorChannels) ? m_rla.NumOfChannelBits + : (c < (m_rla.NumOfColorChannels+m_rla.NumOfMatteBits)) ? m_rla.NumOfMatteBits + : m_rla.NumOfAuxBits; + if (!encode_channel ((unsigned char *)data + offset, pixelsize, + chantype, bits)) return false; + offset += chantype.size(); } return true; diff --git a/testsuite/rla/ref/out.txt b/testsuite/rla/ref/out.txt index 2093c292eb..c22856c7c9 100644 --- a/testsuite/rla/ref/out.txt +++ b/testsuite/rla/ref/out.txt @@ -1,5 +1,5 @@ hi -../../../oiio-testimages/ginsu_a_nc10.rla : 512 x 512, 4 channel, uint16 rla +../../../oiio-images/ginsu_a_nc10.rla : 512 x 512, 4 channel, uint16 rla SHA-1: 2732EF8B7515349C6FB6C8D9B678D9968E304AE3 channel list: R, G, B, A oiio:BitsPerSample: 10 @@ -8,17 +8,17 @@ hi rla:FrameNumber: 1 rla:JobNumber: 1 rla:FileName: "a_nc10.rla" - rla:ProgramName: "IMGLIB" - rla:MachineName: "hawk073.spimageworks.com" - rla:UserName: "jeremys" + Software: "IMGLIB" + HostComputer: "hawk073.spimageworks.com" + Artist: "jeremys" rla:ColorChannel: "rgb" - rla:AspectRatio: 1 + PixelAspectRatio: 1 rla:RedChroma: 0.67 0.33 rla:GreenChroma: 0.21 0.71 rla:BlueChroma: 0.14 0.08 rla:WhitePoint: 0.31 0.316 -Converting ../../../oiio-testimages/ginsu_a_nc10.rla to ginsu_a_nc10.rla -Comparing "../../../oiio-testimages/ginsu_a_nc10.rla" and "ginsu_a_nc10.rla" +Converting ../../../oiio-images/ginsu_a_nc10.rla to ginsu_a_nc10.rla +Comparing "../../../oiio-images/ginsu_a_nc10.rla" and "ginsu_a_nc10.rla" Subimage 0: 512 x 512, 4 channel Mean error = 0 RMS error = 0 @@ -27,7 +27,7 @@ Subimage 0: 512 x 512, 4 channel 0 pixels (0%) over 1e-06 0 pixels (0%) over 1e-06 PASS -../../../oiio-testimages/ginsu_a_ncf.rla : 512 x 512, 4 channel, float rla +../../../oiio-images/ginsu_a_ncf.rla : 512 x 512, 4 channel, float rla SHA-1: 10419E1D3AFFDE7F351BFA9ABF96F8C18527BE5D channel list: R, G, B, A oiio:BitsPerSample: 32 @@ -36,17 +36,17 @@ PASS rla:FrameNumber: 1 rla:JobNumber: 1 rla:FileName: "a_ncf.rla" - rla:ProgramName: "IMGLIB" - rla:MachineName: "hawk073.spimageworks.com" - rla:UserName: "jeremys" + Software: "IMGLIB" + HostComputer: "hawk073.spimageworks.com" + Artist: "jeremys" rla:ColorChannel: "rgb" - rla:AspectRatio: 1 + PixelAspectRatio: 1 rla:RedChroma: 0.67 0.33 rla:GreenChroma: 0.21 0.71 rla:BlueChroma: 0.14 0.08 rla:WhitePoint: 0.31 0.316 -Converting ../../../oiio-testimages/ginsu_a_ncf.rla to ginsu_a_ncf.rla -Comparing "../../../oiio-testimages/ginsu_a_ncf.rla" and "ginsu_a_ncf.rla" +Converting ../../../oiio-images/ginsu_a_ncf.rla to ginsu_a_ncf.rla +Comparing "../../../oiio-images/ginsu_a_ncf.rla" and "ginsu_a_ncf.rla" Subimage 0: 512 x 512, 4 channel Mean error = 0 RMS error = 0 @@ -55,7 +55,7 @@ Subimage 0: 512 x 512, 4 channel 0 pixels (0%) over 1e-06 0 pixels (0%) over 1e-06 PASS -../../../oiio-testimages/ginsu_rgba_nc8.rla : 512 x 512, 4 channel, uint8 rla +../../../oiio-images/ginsu_rgba_nc8.rla : 512 x 512, 4 channel, uint8 rla SHA-1: 030CF1F960E9585D2F6A1173B86034BC15C26C41 channel list: R, G, B, A oiio:BitsPerSample: 8 @@ -64,17 +64,17 @@ PASS rla:FrameNumber: 1 rla:JobNumber: 1 rla:FileName: "rgba_nc8.rla" - rla:ProgramName: "IMGLIB" - rla:MachineName: "hawk073.spimageworks.com" - rla:UserName: "jeremys" + Software: "IMGLIB" + HostComputer: "hawk073.spimageworks.com" + Artist: "jeremys" rla:ColorChannel: "rgb" - rla:AspectRatio: 1 + PixelAspectRatio: 1 rla:RedChroma: 0.67 0.33 rla:GreenChroma: 0.21 0.71 rla:BlueChroma: 0.14 0.08 rla:WhitePoint: 0.31 0.316 -Converting ../../../oiio-testimages/ginsu_rgba_nc8.rla to ginsu_rgba_nc8.rla -Comparing "../../../oiio-testimages/ginsu_rgba_nc8.rla" and "ginsu_rgba_nc8.rla" +Converting ../../../oiio-images/ginsu_rgba_nc8.rla to ginsu_rgba_nc8.rla +Comparing "../../../oiio-images/ginsu_rgba_nc8.rla" and "ginsu_rgba_nc8.rla" Subimage 0: 512 x 512, 4 channel Mean error = 0 RMS error = 0 @@ -83,7 +83,7 @@ Subimage 0: 512 x 512, 4 channel 0 pixels (0%) over 1e-06 0 pixels (0%) over 1e-06 PASS -../../../oiio-testimages/ginsu_rgb_nc16.rla : 512 x 512, 3 channel, uint16 rla +../../../oiio-images/ginsu_rgb_nc16.rla : 512 x 512, 3 channel, uint16 rla SHA-1: B31B6515AA9BD09C8CFFCB242C1E7D5D130CA923 channel list: R, G, B oiio:BitsPerSample: 16 @@ -92,17 +92,17 @@ PASS rla:FrameNumber: 1 rla:JobNumber: 1 rla:FileName: "rgb_nc16.rla" - rla:ProgramName: "IMGLIB" - rla:MachineName: "hawk073.spimageworks.com" - rla:UserName: "jeremys" + Software: "IMGLIB" + HostComputer: "hawk073.spimageworks.com" + Artist: "jeremys" rla:ColorChannel: "rgb" - rla:AspectRatio: 1 + PixelAspectRatio: 1 rla:RedChroma: 0.67 0.33 rla:GreenChroma: 0.21 0.71 rla:BlueChroma: 0.14 0.08 rla:WhitePoint: 0.31 0.316 -Converting ../../../oiio-testimages/ginsu_rgb_nc16.rla to ginsu_rgb_nc16.rla -Comparing "../../../oiio-testimages/ginsu_rgb_nc16.rla" and "ginsu_rgb_nc16.rla" +Converting ../../../oiio-images/ginsu_rgb_nc16.rla to ginsu_rgb_nc16.rla +Comparing "../../../oiio-images/ginsu_rgb_nc16.rla" and "ginsu_rgb_nc16.rla" Subimage 0: 512 x 512, 3 channel Mean error = 0 RMS error = 0 @@ -111,15 +111,15 @@ Subimage 0: 512 x 512, 3 channel 0 pixels (0%) over 1e-06 0 pixels (0%) over 1e-06 PASS -../../../oiio-testimages/imgmake_rgba_nc10.rla : 512 x 512, 4 channel, uint16 rla +../../../oiio-images/imgmake_rgba_nc10.rla : 512 x 512, 4 channel, uint16 rla SHA-1: 12F22B420DDB3D330092A38AF48F9A225467EC6E channel list: R, G, B, A oiio:BitsPerSample: 10 compression: "rle" rla:FrameNumber: 1 rla:FileName: "imgmake_rgba_nc10.rla" - rla:MachineName: "hawk073.spimageworks.com" - rla:UserName: "jeremys" + HostComputer: "hawk073.spimageworks.com" + Artist: "jeremys" rla:Aspect: " 1.333" rla:ColorChannel: "rgb" oiio:ColorSpace: "Linear" @@ -127,8 +127,8 @@ PASS rla:GreenChroma: 0.21 0.21 0.71 rla:BlueChroma: 0.14 0.14 0.08 rla:WhitePoint: 0.31 0.31 0.316 -Converting ../../../oiio-testimages/imgmake_rgba_nc10.rla to imgmake_rgba_nc10.rla -Comparing "../../../oiio-testimages/imgmake_rgba_nc10.rla" and "imgmake_rgba_nc10.rla" +Converting ../../../oiio-images/imgmake_rgba_nc10.rla to imgmake_rgba_nc10.rla +Comparing "../../../oiio-images/imgmake_rgba_nc10.rla" and "imgmake_rgba_nc10.rla" Subimage 0: 512 x 512, 4 channel Mean error = 0 RMS error = 0 @@ -137,7 +137,7 @@ Subimage 0: 512 x 512, 4 channel 0 pixels (0%) over 1e-06 0 pixels (0%) over 1e-06 PASS -../../../oiio-testimages/ginsu_a_nc16.rla : 512 x 512, 4 channel, uint16 rla +../../../oiio-images/ginsu_a_nc16.rla : 512 x 512, 4 channel, uint16 rla SHA-1: 476760230A4F7540072865C0E01CC6417EA9EC3E channel list: R, G, B, A oiio:BitsPerSample: 16 @@ -146,17 +146,17 @@ PASS rla:FrameNumber: 1 rla:JobNumber: 1 rla:FileName: "a_nc16.rla" - rla:ProgramName: "IMGLIB" - rla:MachineName: "hawk073.spimageworks.com" - rla:UserName: "jeremys" + Software: "IMGLIB" + HostComputer: "hawk073.spimageworks.com" + Artist: "jeremys" rla:ColorChannel: "rgb" - rla:AspectRatio: 1 + PixelAspectRatio: 1 rla:RedChroma: 0.67 0.33 rla:GreenChroma: 0.21 0.71 rla:BlueChroma: 0.14 0.08 rla:WhitePoint: 0.31 0.316 -Converting ../../../oiio-testimages/ginsu_a_nc16.rla to ginsu_a_nc16.rla -Comparing "../../../oiio-testimages/ginsu_a_nc16.rla" and "ginsu_a_nc16.rla" +Converting ../../../oiio-images/ginsu_a_nc16.rla to ginsu_a_nc16.rla +Comparing "../../../oiio-images/ginsu_a_nc16.rla" and "ginsu_a_nc16.rla" Subimage 0: 512 x 512, 4 channel Mean error = 0 RMS error = 0 @@ -165,7 +165,7 @@ Subimage 0: 512 x 512, 4 channel 0 pixels (0%) over 1e-06 0 pixels (0%) over 1e-06 PASS -../../../oiio-testimages/ginsu_rgba_nc10.rla : 512 x 512, 4 channel, uint16 rla +../../../oiio-images/ginsu_rgba_nc10.rla : 512 x 512, 4 channel, uint16 rla SHA-1: 12F22B420DDB3D330092A38AF48F9A225467EC6E channel list: R, G, B, A oiio:BitsPerSample: 10 @@ -174,17 +174,17 @@ PASS rla:FrameNumber: 1 rla:JobNumber: 1 rla:FileName: "rgba_nc10.rla" - rla:ProgramName: "IMGLIB" - rla:MachineName: "hawk073.spimageworks.com" - rla:UserName: "jeremys" + Software: "IMGLIB" + HostComputer: "hawk073.spimageworks.com" + Artist: "jeremys" rla:ColorChannel: "rgb" - rla:AspectRatio: 1 + PixelAspectRatio: 1 rla:RedChroma: 0.67 0.33 rla:GreenChroma: 0.21 0.71 rla:BlueChroma: 0.14 0.08 rla:WhitePoint: 0.31 0.316 -Converting ../../../oiio-testimages/ginsu_rgba_nc10.rla to ginsu_rgba_nc10.rla -Comparing "../../../oiio-testimages/ginsu_rgba_nc10.rla" and "ginsu_rgba_nc10.rla" +Converting ../../../oiio-images/ginsu_rgba_nc10.rla to ginsu_rgba_nc10.rla +Comparing "../../../oiio-images/ginsu_rgba_nc10.rla" and "ginsu_rgba_nc10.rla" Subimage 0: 512 x 512, 4 channel Mean error = 0 RMS error = 0 @@ -193,7 +193,7 @@ Subimage 0: 512 x 512, 4 channel 0 pixels (0%) over 1e-06 0 pixels (0%) over 1e-06 PASS -../../../oiio-testimages/ginsu_rgba_ncf.rla : 512 x 512, 4 channel, float rla +../../../oiio-images/ginsu_rgba_ncf.rla : 512 x 512, 4 channel, float rla SHA-1: 43EB1CE263C8FC4630AEE68E34139C02A13F506D channel list: R, G, B, A oiio:BitsPerSample: 32 @@ -202,17 +202,17 @@ PASS rla:FrameNumber: 1 rla:JobNumber: 1 rla:FileName: "rgba_ncf.rla" - rla:ProgramName: "IMGLIB" - rla:MachineName: "hawk073.spimageworks.com" - rla:UserName: "jeremys" + Software: "IMGLIB" + HostComputer: "hawk073.spimageworks.com" + Artist: "jeremys" rla:ColorChannel: "rgb" - rla:AspectRatio: 1 + PixelAspectRatio: 1 rla:RedChroma: 0.67 0.33 rla:GreenChroma: 0.21 0.71 rla:BlueChroma: 0.14 0.08 rla:WhitePoint: 0.31 0.316 -Converting ../../../oiio-testimages/ginsu_rgba_ncf.rla to ginsu_rgba_ncf.rla -Comparing "../../../oiio-testimages/ginsu_rgba_ncf.rla" and "ginsu_rgba_ncf.rla" +Converting ../../../oiio-images/ginsu_rgba_ncf.rla to ginsu_rgba_ncf.rla +Comparing "../../../oiio-images/ginsu_rgba_ncf.rla" and "ginsu_rgba_ncf.rla" Subimage 0: 512 x 512, 4 channel Mean error = 0 RMS error = 0 @@ -221,7 +221,7 @@ Subimage 0: 512 x 512, 4 channel 0 pixels (0%) over 1e-06 0 pixels (0%) over 1e-06 PASS -../../../oiio-testimages/ginsu_rgb_nc8.rla : 512 x 512, 3 channel, uint8 rla +../../../oiio-images/ginsu_rgb_nc8.rla : 512 x 512, 3 channel, uint8 rla SHA-1: CB23AB13150BBD29947E65434E174D76C0324E7D channel list: R, G, B oiio:BitsPerSample: 8 @@ -230,17 +230,17 @@ PASS rla:FrameNumber: 1 rla:JobNumber: 1 rla:FileName: "rgb_nc8.rla" - rla:ProgramName: "IMGLIB" - rla:MachineName: "hawk073.spimageworks.com" - rla:UserName: "jeremys" + Software: "IMGLIB" + HostComputer: "hawk073.spimageworks.com" + Artist: "jeremys" rla:ColorChannel: "rgb" - rla:AspectRatio: 1 + PixelAspectRatio: 1 rla:RedChroma: 0.67 0.33 rla:GreenChroma: 0.21 0.71 rla:BlueChroma: 0.14 0.08 rla:WhitePoint: 0.31 0.316 -Converting ../../../oiio-testimages/ginsu_rgb_nc8.rla to ginsu_rgb_nc8.rla -Comparing "../../../oiio-testimages/ginsu_rgb_nc8.rla" and "ginsu_rgb_nc8.rla" +Converting ../../../oiio-images/ginsu_rgb_nc8.rla to ginsu_rgb_nc8.rla +Comparing "../../../oiio-images/ginsu_rgb_nc8.rla" and "ginsu_rgb_nc8.rla" Subimage 0: 512 x 512, 3 channel Mean error = 0 RMS error = 0 @@ -249,15 +249,15 @@ Subimage 0: 512 x 512, 3 channel 0 pixels (0%) over 1e-06 0 pixels (0%) over 1e-06 PASS -../../../oiio-testimages/imgmake_rgba_nc16.rla : 512 x 512, 4 channel, uint16 rla +../../../oiio-images/imgmake_rgba_nc16.rla : 512 x 512, 4 channel, uint16 rla SHA-1: C1E653EAAFABEE98913D9B485CADAA50EF5731B2 channel list: R, G, B, A oiio:BitsPerSample: 16 compression: "rle" rla:FrameNumber: 1 rla:FileName: "imgmake_rgba_nc16.rla" - rla:MachineName: "hawk073.spimageworks.com" - rla:UserName: "jeremys" + HostComputer: "hawk073.spimageworks.com" + Artist: "jeremys" rla:Aspect: " 1.333" rla:ColorChannel: "rgb" oiio:ColorSpace: "Linear" @@ -265,8 +265,8 @@ PASS rla:GreenChroma: 0.21 0.21 0.71 rla:BlueChroma: 0.14 0.14 0.08 rla:WhitePoint: 0.31 0.31 0.316 -Converting ../../../oiio-testimages/imgmake_rgba_nc16.rla to imgmake_rgba_nc16.rla -Comparing "../../../oiio-testimages/imgmake_rgba_nc16.rla" and "imgmake_rgba_nc16.rla" +Converting ../../../oiio-images/imgmake_rgba_nc16.rla to imgmake_rgba_nc16.rla +Comparing "../../../oiio-images/imgmake_rgba_nc16.rla" and "imgmake_rgba_nc16.rla" Subimage 0: 512 x 512, 4 channel Mean error = 0 RMS error = 0 @@ -275,7 +275,7 @@ Subimage 0: 512 x 512, 4 channel 0 pixels (0%) over 1e-06 0 pixels (0%) over 1e-06 PASS -../../../oiio-testimages/ginsu_a_nc8.rla : 512 x 512, 4 channel, uint8 rla +../../../oiio-images/ginsu_a_nc8.rla : 512 x 512, 4 channel, uint8 rla SHA-1: B96EF7B8A3B5328312BE6EA090A408681C423A00 channel list: R, G, B, A oiio:BitsPerSample: 8 @@ -284,17 +284,17 @@ PASS rla:FrameNumber: 1 rla:JobNumber: 1 rla:FileName: "a_nc8.rla" - rla:ProgramName: "IMGLIB" - rla:MachineName: "hawk073.spimageworks.com" - rla:UserName: "jeremys" + Software: "IMGLIB" + HostComputer: "hawk073.spimageworks.com" + Artist: "jeremys" rla:ColorChannel: "rgb" - rla:AspectRatio: 1 + PixelAspectRatio: 1 rla:RedChroma: 0.67 0.33 rla:GreenChroma: 0.21 0.71 rla:BlueChroma: 0.14 0.08 rla:WhitePoint: 0.31 0.316 -Converting ../../../oiio-testimages/ginsu_a_nc8.rla to ginsu_a_nc8.rla -Comparing "../../../oiio-testimages/ginsu_a_nc8.rla" and "ginsu_a_nc8.rla" +Converting ../../../oiio-images/ginsu_a_nc8.rla to ginsu_a_nc8.rla +Comparing "../../../oiio-images/ginsu_a_nc8.rla" and "ginsu_a_nc8.rla" Subimage 0: 512 x 512, 4 channel Mean error = 0 RMS error = 0 @@ -303,7 +303,7 @@ Subimage 0: 512 x 512, 4 channel 0 pixels (0%) over 1e-06 0 pixels (0%) over 1e-06 PASS -../../../oiio-testimages/ginsu_rgba_nc16.rla : 512 x 512, 4 channel, uint16 rla +../../../oiio-images/ginsu_rgba_nc16.rla : 512 x 512, 4 channel, uint16 rla SHA-1: C1E653EAAFABEE98913D9B485CADAA50EF5731B2 channel list: R, G, B, A oiio:BitsPerSample: 16 @@ -312,17 +312,17 @@ PASS rla:FrameNumber: 1 rla:JobNumber: 1 rla:FileName: "rgba_nc16.rla" - rla:ProgramName: "IMGLIB" - rla:MachineName: "hawk073.spimageworks.com" - rla:UserName: "jeremys" + Software: "IMGLIB" + HostComputer: "hawk073.spimageworks.com" + Artist: "jeremys" rla:ColorChannel: "rgb" - rla:AspectRatio: 1 + PixelAspectRatio: 1 rla:RedChroma: 0.67 0.33 rla:GreenChroma: 0.21 0.71 rla:BlueChroma: 0.14 0.08 rla:WhitePoint: 0.31 0.316 -Converting ../../../oiio-testimages/ginsu_rgba_nc16.rla to ginsu_rgba_nc16.rla -Comparing "../../../oiio-testimages/ginsu_rgba_nc16.rla" and "ginsu_rgba_nc16.rla" +Converting ../../../oiio-images/ginsu_rgba_nc16.rla to ginsu_rgba_nc16.rla +Comparing "../../../oiio-images/ginsu_rgba_nc16.rla" and "ginsu_rgba_nc16.rla" Subimage 0: 512 x 512, 4 channel Mean error = 0 RMS error = 0 @@ -331,7 +331,7 @@ Subimage 0: 512 x 512, 4 channel 0 pixels (0%) over 1e-06 0 pixels (0%) over 1e-06 PASS -../../../oiio-testimages/ginsu_rgb_nc10.rla : 512 x 512, 3 channel, uint16 rla +../../../oiio-images/ginsu_rgb_nc10.rla : 512 x 512, 3 channel, uint16 rla SHA-1: CF6AE7239E7F8FEE3D44BD6D6FE907642938B471 channel list: R, G, B oiio:BitsPerSample: 10 @@ -340,17 +340,17 @@ PASS rla:FrameNumber: 1 rla:JobNumber: 1 rla:FileName: "rgb_nc10.rla" - rla:ProgramName: "IMGLIB" - rla:MachineName: "hawk073.spimageworks.com" - rla:UserName: "jeremys" + Software: "IMGLIB" + HostComputer: "hawk073.spimageworks.com" + Artist: "jeremys" rla:ColorChannel: "rgb" - rla:AspectRatio: 1 + PixelAspectRatio: 1 rla:RedChroma: 0.67 0.33 rla:GreenChroma: 0.21 0.71 rla:BlueChroma: 0.14 0.08 rla:WhitePoint: 0.31 0.316 -Converting ../../../oiio-testimages/ginsu_rgb_nc10.rla to ginsu_rgb_nc10.rla -Comparing "../../../oiio-testimages/ginsu_rgb_nc10.rla" and "ginsu_rgb_nc10.rla" +Converting ../../../oiio-images/ginsu_rgb_nc10.rla to ginsu_rgb_nc10.rla +Comparing "../../../oiio-images/ginsu_rgb_nc10.rla" and "ginsu_rgb_nc10.rla" Subimage 0: 512 x 512, 3 channel Mean error = 0 RMS error = 0 @@ -359,7 +359,7 @@ Subimage 0: 512 x 512, 3 channel 0 pixels (0%) over 1e-06 0 pixels (0%) over 1e-06 PASS -../../../oiio-testimages/ginsu_rgb_ncf.rla : 512 x 512, 3 channel, float rla +../../../oiio-images/ginsu_rgb_ncf.rla : 512 x 512, 3 channel, float rla SHA-1: B5499846C3D45BF41351BC7A7338E9A31DD883AD channel list: R, G, B oiio:BitsPerSample: 32 @@ -368,17 +368,17 @@ PASS rla:FrameNumber: 1 rla:JobNumber: 1 rla:FileName: "rgb_ncf.rla" - rla:ProgramName: "IMGLIB" - rla:MachineName: "hawk073.spimageworks.com" - rla:UserName: "jeremys" + Software: "IMGLIB" + HostComputer: "hawk073.spimageworks.com" + Artist: "jeremys" rla:ColorChannel: "rgb" - rla:AspectRatio: 1 + PixelAspectRatio: 1 rla:RedChroma: 0.67 0.33 rla:GreenChroma: 0.21 0.71 rla:BlueChroma: 0.14 0.08 rla:WhitePoint: 0.31 0.316 -Converting ../../../oiio-testimages/ginsu_rgb_ncf.rla to ginsu_rgb_ncf.rla -Comparing "../../../oiio-testimages/ginsu_rgb_ncf.rla" and "ginsu_rgb_ncf.rla" +Converting ../../../oiio-images/ginsu_rgb_ncf.rla to ginsu_rgb_ncf.rla +Comparing "../../../oiio-images/ginsu_rgb_ncf.rla" and "ginsu_rgb_ncf.rla" Subimage 0: 512 x 512, 3 channel Mean error = 0 RMS error = 0 @@ -387,15 +387,15 @@ Subimage 0: 512 x 512, 3 channel 0 pixels (0%) over 1e-06 0 pixels (0%) over 1e-06 PASS -../../../oiio-testimages/imgmake_rgba_nc8.rla : 512 x 512, 4 channel, uint8 rla +../../../oiio-images/imgmake_rgba_nc8.rla : 512 x 512, 4 channel, uint8 rla SHA-1: 030CF1F960E9585D2F6A1173B86034BC15C26C41 channel list: R, G, B, A oiio:BitsPerSample: 8 compression: "rle" rla:FrameNumber: 1 rla:FileName: "imgmake_rgba_nc8.rla" - rla:MachineName: "hawk073.spimageworks.com" - rla:UserName: "jeremys" + HostComputer: "hawk073.spimageworks.com" + Artist: "jeremys" rla:Aspect: " 1.333" rla:ColorChannel: "rgb" oiio:ColorSpace: "Linear" @@ -403,8 +403,8 @@ PASS rla:GreenChroma: 0.21 0.21 0.71 rla:BlueChroma: 0.14 0.14 0.08 rla:WhitePoint: 0.31 0.31 0.316 -Converting ../../../oiio-testimages/imgmake_rgba_nc8.rla to imgmake_rgba_nc8.rla -Comparing "../../../oiio-testimages/imgmake_rgba_nc8.rla" and "imgmake_rgba_nc8.rla" +Converting ../../../oiio-images/imgmake_rgba_nc8.rla to imgmake_rgba_nc8.rla +Comparing "../../../oiio-images/imgmake_rgba_nc8.rla" and "imgmake_rgba_nc8.rla" Subimage 0: 512 x 512, 4 channel Mean error = 0 RMS error = 0