1 // Copyright 2008-present Contributors to the OpenImageIO project.
2 // SPDX-License-Identifier: BSD-3-Clause
3 // https://github.com/OpenImageIO/oiio/blob/master/LICENSE.md
4 
5 #include <cassert>
6 #include <cmath>
7 #include <cstdio>
8 #include <cstdlib>
9 
10 #include <OpenImageIO/dassert.h>
11 #include <OpenImageIO/filesystem.h>
12 #include <OpenImageIO/imagebufalgo_util.h>
13 #include <OpenImageIO/imageio.h>
14 
15 #include "rla_pvt.h"
16 
17 OIIO_PLUGIN_NAMESPACE_BEGIN
18 
19 using namespace RLA_pvt;
20 
21 
22 class RLAInput final : public ImageInput {
23 public:
RLAInput()24     RLAInput() { init(); }
~RLAInput()25     virtual ~RLAInput() { close(); }
format_name(void) const26     virtual const char* format_name(void) const override { return "rla"; }
27     virtual bool open(const std::string& name, ImageSpec& newspec) override;
current_subimage(void) const28     virtual int current_subimage(void) const override
29     {
30         lock_guard lock(m_mutex);
31         return m_subimage;
32     }
33     virtual bool seek_subimage(int subimage, int miplevel) override;
34     virtual bool close() override;
35     virtual bool read_native_scanline(int subimage, int miplevel, int y, int z,
36                                       void* data) override;
37 
38 private:
39     std::string m_filename;            ///< Stash the filename
40     FILE* m_file;                      ///< Open image handle
41     RLAHeader m_rla;                   ///< Wavefront RLA header
42     std::vector<unsigned char> m_buf;  ///< Buffer the image pixels
43     int m_subimage;                    ///< Current subimage index
44     std::vector<uint32_t> m_sot;       ///< Scanline offsets table
45     int m_stride;                      ///< Number of bytes a contig pixel takes
46 
47     /// Reset everything to initial state
48     ///
init()49     void init()
50     {
51         m_file = NULL;
52         m_buf.clear();
53     }
54 
55     /// Helper: raw read, with error detection
56     ///
fread(void * buf,size_t itemsize,size_t nitems)57     bool fread(void* buf, size_t itemsize, size_t nitems)
58     {
59         size_t n = ::fread(buf, itemsize, nitems, m_file);
60         if (n != nitems)
61             errorf("Read error: read %d records but %d expected %s", (int)n,
62                    (int)nitems, feof(m_file) ? " (hit EOF)" : "");
63         return n == nitems;
64     }
65 
66     /// Helper: read buf[0..nitems-1], swap endianness if necessary
read(T * buf,size_t nitems=1)67     template<typename T> bool read(T* buf, size_t nitems = 1)
68     {
69         if (!fread(buf, sizeof(T), nitems))
70             return false;
71         if (littleendian()
72             && (is_same<T, uint16_t>::value || is_same<T, int16_t>::value
73                 || is_same<T, uint32_t>::value || is_same<T, int32_t>::value)) {
74             swap_endian(buf, nitems);
75         }
76         return true;
77     }
78 
79     /// Helper function: translate 3-letter month abbreviation to number.
80     ///
81     inline int get_month_number(const char* s);
82 
83     /// Helper: read the RLA header and scanline offset table.
84     ///
85     inline bool read_header();
86 
87     /// Helper: read and decode a single channel group consisting of
88     /// channels [first_channel .. first_channel+num_channels-1], which
89     /// all share the same number of significant bits.
90     bool decode_channel_group(int first_channel, short num_channels,
91                               short num_bits, int y);
92 
93     /// Helper: decode a span of n RLE-encoded bytes from encoded[0..elen-1]
94     /// into buf[0],buf[stride],buf[2*stride]...buf[(n-1)*stride].
95     /// Return the number of encoded bytes we ate to fill buf.
96     size_t decode_rle_span(unsigned char* buf, int n, int stride,
97                            const char* encoded, size_t elen);
98 
99     /// Helper: determine channel TypeDesc
100     inline TypeDesc get_channel_typedesc(short chan_type, short chan_bits);
101 
102     // debugging aid
preview(std::ostream & out)103     void preview(std::ostream& out)
104     {
105         OIIO_DASSERT(!feof(m_file));
106         long pos = ftell(m_file);
107         out << "@" << pos << ", next 4 bytes are ";
108         union {  // trickery to avoid punned pointer warnings
109             unsigned char c[4];
110             uint16_t s[2];
111             uint32_t i;
112         } u;
113         read(&u.c, 4);  // because it's char, it didn't swap endian
114         uint16_t s[2] = { u.s[0], u.s[1] };
115         uint32_t i    = u.i;
116         if (littleendian()) {
117             swap_endian(s, 2);
118             swap_endian(&i);
119         }
120         out << Strutil::sprintf("%d/%u %d/%u %d/%u %d/%u (%d %d) (%u)\n",
121                                 u.c[0], ((char*)u.c)[0], u.c[1],
122                                 ((char*)u.c)[1], u.c[2], ((char*)u.c)[2],
123                                 u.c[3], ((char*)u.c)[3], s[0], s[1], i);
124         fseek(m_file, pos, SEEK_SET);
125     }
126 };
127 
128 
129 
130 // Obligatory material to make this a recognizeable imageio plugin:
131 OIIO_PLUGIN_EXPORTS_BEGIN
132 
133 OIIO_EXPORT ImageInput*
rla_input_imageio_create()134 rla_input_imageio_create()
135 {
136     return new RLAInput;
137 }
138 
139 OIIO_EXPORT int rla_imageio_version = OIIO_PLUGIN_VERSION;
140 
141 OIIO_EXPORT const char*
rla_imageio_library_version()142 rla_imageio_library_version()
143 {
144     return nullptr;
145 }
146 
147 OIIO_EXPORT const char* rla_input_extensions[] = { "rla", nullptr };
148 
149 OIIO_PLUGIN_EXPORTS_END
150 
151 
152 
153 bool
open(const std::string & name,ImageSpec & newspec)154 RLAInput::open(const std::string& name, ImageSpec& newspec)
155 {
156     m_filename = name;
157 
158     m_file = Filesystem::fopen(name, "rb");
159     if (!m_file) {
160         errorf("Could not open file \"%s\"", name);
161         return false;
162     }
163 
164     // set a bogus subimage index so that seek_subimage actually seeks
165     m_subimage = 1;
166 
167     bool ok = seek_subimage(0, 0);
168     newspec = spec();
169     return ok;
170 }
171 
172 
173 
174 inline bool
read_header()175 RLAInput::read_header()
176 {
177     // Read the image header, which should have the same exact layout as
178     // the m_rla structure (except for endianness issues).
179     static_assert(sizeof(m_rla) == 740, "Bad RLA struct size");
180     if (!read(&m_rla)) {
181         errorf("RLA could not read the image header");
182         return false;
183     }
184     m_rla.rla_swap_endian();  // fix endianness
185 
186     if (m_rla.Revision != (int16_t)0xFFFE
187         && m_rla.Revision != 0 /* for some reason, this can happen */) {
188         errorf("RLA header Revision number unrecognized: %d", m_rla.Revision);
189         return false;  // unknown file revision
190     }
191     if (m_rla.NumOfChannelBits < 0 || m_rla.NumOfChannelBits > 32
192         || m_rla.NumOfMatteBits < 0 || m_rla.NumOfMatteBits > 32
193         || m_rla.NumOfAuxBits < 0 || m_rla.NumOfAuxBits > 32) {
194         errorf("Unsupported bit depth, or maybe corrupted file.");
195         return false;
196     }
197     if (m_rla.NumOfChannelBits == 0)
198         m_rla.NumOfChannelBits = 8;  // apparently, this can happen
199 
200     // Immediately following the header is the scanline offset table --
201     // one uint32_t for each scanline, giving absolute offsets (from the
202     // beginning of the file) where the RLE records start for each
203     // scanline of this subimage.
204     m_sot.resize(std::abs(m_rla.ActiveBottom - m_rla.ActiveTop) + 1, 0);
205     if (!read(&m_sot[0], m_sot.size())) {
206         errorf("RLA could not read the scanline offset table");
207         return false;
208     }
209     return true;
210 }
211 
212 
213 
214 bool
seek_subimage(int subimage,int miplevel)215 RLAInput::seek_subimage(int subimage, int miplevel)
216 {
217     if (miplevel != 0 || subimage < 0)
218         return false;
219 
220     if (subimage == current_subimage())
221         return true;  // already on the right level
222 
223     // RLA images allow multiple subimages; they are simply concatenated
224     // together, wth image N's header field NextOffset giving the
225     // absolute offset of the start of image N+1.
226     int diff = subimage - current_subimage();
227     if (subimage - current_subimage() < 0) {
228         // If we are requesting an image earlier than the current one,
229         // reset to the first subimage.
230         fseek(m_file, 0, SEEK_SET);
231         if (!read_header())
232             return false;  // read_header always calls error()
233         diff = subimage;
234     }
235     // forward scrolling -- skip subimages until we're at the right place
236     while (diff > 0 && m_rla.NextOffset != 0) {
237         if (!fseek(m_file, m_rla.NextOffset, SEEK_SET)) {
238             errorf("Could not seek to header offset. Corrupted file?");
239             return false;
240         }
241         if (!read_header())
242             return false;  // read_header always calls error()
243         --diff;
244     }
245     if (diff > 0 && m_rla.NextOffset == 0) {  // no more subimages to read
246         errorf("Unknown subimage");
247         return false;
248     }
249 
250     // Now m_rla holds the header of the requested subimage.  Examine it
251     // to fill out our ImageSpec.
252 
253     if (m_rla.ColorChannelType < 0 || m_rla.ColorChannelType > CT_FLOAT) {
254         errorf("Illegal color channel type: %d", m_rla.ColorChannelType);
255         return false;
256     }
257     if (m_rla.MatteChannelType < 0 || m_rla.MatteChannelType > CT_FLOAT) {
258         errorf("Illegal matte channel type: %d", m_rla.MatteChannelType);
259         return false;
260     }
261     if (m_rla.AuxChannelType < 0 || m_rla.AuxChannelType > CT_FLOAT) {
262         errorf("Illegal auxiliary channel type: %d", m_rla.AuxChannelType);
263         return false;
264     }
265 
266     // pick maximum precision for the time being
267     TypeDesc col_type = get_channel_typedesc(m_rla.ColorChannelType,
268                                              m_rla.NumOfChannelBits);
269     TypeDesc mat_type = m_rla.NumOfMatteChannels
270                             ? get_channel_typedesc(m_rla.MatteChannelType,
271                                                    m_rla.NumOfMatteBits)
272                             : TypeUnknown;
273     TypeDesc aux_type = m_rla.NumOfAuxChannels
274                             ? get_channel_typedesc(m_rla.AuxChannelType,
275                                                    m_rla.NumOfAuxBits)
276                             : TypeUnknown;
277     TypeDesc maxtype  = ImageBufAlgo::type_merge(col_type, mat_type, aux_type);
278     if (maxtype == TypeUnknown) {
279         errorf("Failed channel bytes sanity check");
280         return false;  // failed sanity check
281     }
282 
283     if (m_rla.NumOfColorChannels < 1 || m_rla.NumOfColorChannels > 3
284         || m_rla.NumOfMatteChannels < 0 || m_rla.NumOfMatteChannels > 3
285         || m_rla.NumOfAuxChannels < 0 || m_rla.NumOfAuxChannels > 256) {
286         errorf(
287             "Invalid number of channels (%d color, %d matte, %d aux), or corrupted header.",
288             m_rla.NumOfColorChannels, m_rla.NumOfMatteChannels,
289             m_rla.NumOfAuxChannels);
290         return false;
291     }
292     m_spec = ImageSpec(m_rla.ActiveRight - m_rla.ActiveLeft + 1,
293                        (m_rla.ActiveTop - m_rla.ActiveBottom + 1)
294                            / (m_rla.FieldRendered ? 2 : 1),  // interlaced image?
295                        m_rla.NumOfColorChannels + m_rla.NumOfMatteChannels
296                            + m_rla.NumOfAuxChannels,
297                        maxtype);
298 
299     // set window dimensions etc.
300     m_spec.x           = m_rla.ActiveLeft;
301     m_spec.y           = m_spec.height - 1 - m_rla.ActiveTop;
302     m_spec.full_width  = m_rla.WindowRight - m_rla.WindowLeft + 1;
303     m_spec.full_height = m_rla.WindowTop - m_rla.WindowBottom + 1;
304     m_spec.full_depth  = 1;
305     m_spec.full_x      = m_rla.WindowLeft;
306     m_spec.full_y      = m_spec.full_height - 1 - m_rla.WindowTop;
307 
308     // set channel formats and stride
309     int z_channel = -1;
310     m_stride      = 0;
311     for (int i = 0; i < m_rla.NumOfColorChannels; ++i)
312         m_spec.channelformats.push_back(col_type);
313     m_stride += m_rla.NumOfColorChannels * col_type.size();
314     for (int i = 0; i < m_rla.NumOfMatteChannels; ++i)
315         m_spec.channelformats.push_back(mat_type);
316     if (m_rla.NumOfMatteChannels >= 1)
317         m_spec.alpha_channel = m_rla.NumOfColorChannels;
318     else
319         m_spec.alpha_channel = -1;
320     m_stride += m_rla.NumOfMatteChannels * mat_type.size();
321     for (int i = 0; i < m_rla.NumOfAuxChannels; ++i) {
322         m_spec.channelformats.push_back(aux_type);
323         // assume first float aux or 32 bit int channel is z
324         if (z_channel < 0
325             && (aux_type == TypeDesc::FLOAT || aux_type == TypeDesc::INT32
326                 || aux_type == TypeDesc::UINT32)) {
327             z_channel = m_rla.NumOfColorChannels + m_rla.NumOfMatteChannels;
328             m_spec.z_channel               = z_channel;
329             m_spec.channelnames[z_channel] = "Z";
330         }
331     }
332     m_stride += m_rla.NumOfAuxChannels * aux_type.size();
333 
334     // But if all channels turned out the same, just use 'format' and don't
335     // bother sending back channelformats at all.
336     bool allsame = true;
337     for (int c = 1; c < m_spec.nchannels; ++c)
338         allsame &= (m_spec.channelformats[c] == m_spec.channelformats[0]);
339     if (allsame) {
340         m_spec.format = m_spec.channelformats[0];
341         m_spec.channelformats.clear();
342         m_spec.attribute("oiio:BitsPerSample", m_rla.NumOfChannelBits);
343         // N.B. don't set bps for mixed formats, it isn't well defined
344     }
345 
346     // this is always true
347     m_spec.attribute("compression", "rle");
348 
349     if (m_rla.DateCreated[0]) {
350         char month[4] = { 0, 0, 0, 0 };
351         int d, h, M, m, y;
352         if (sscanf(m_rla.DateCreated, "%c%c%c %d %d:%d %d", month + 0,
353                    month + 1, month + 2, &d, &h, &m, &y)
354             == 7) {
355             M = get_month_number(month);
356             if (M > 0) {
357                 // construct a date/time marker in OIIO convention
358                 m_spec.attribute("DateTime",
359                                  Strutil::sprintf("%4d:%02d:%02d %02d:%02d:00",
360                                                   y, M, d, h, m));
361             }
362         }
363     }
364 
365     // save some typing by using macros
366 #define FIELD(x, name) \
367     if (m_rla.x > 0)   \
368     m_spec.attribute(name, m_rla.x)
369 #define STRING_FIELD(x, name) \
370     if (m_rla.x[0])           \
371     m_spec.attribute(name, m_rla.x)
372 
373     STRING_FIELD(Description, "ImageDescription");
374     FIELD(FrameNumber, "rla:FrameNumber");
375     FIELD(Revision, "rla:Revision");
376     FIELD(JobNumber, "rla:JobNumber");
377     FIELD(FieldRendered, "rla:FieldRendered");
378     STRING_FIELD(FileName, "rla:FileName");
379     STRING_FIELD(ProgramName, "Software");
380     STRING_FIELD(MachineName, "HostComputer");
381     STRING_FIELD(UserName, "Artist");
382     STRING_FIELD(Aspect, "rla:Aspect");
383     STRING_FIELD(ColorChannel, "rla:ColorChannel");
384     STRING_FIELD(Time, "rla:Time");
385     STRING_FIELD(Filter, "rla:Filter");
386     STRING_FIELD(AuxData, "rla:AuxData");
387 #undef STRING_FIELD
388 #undef FIELD
389 
390     float gamma = Strutil::from_string<float>(m_rla.Gamma);
391     if (gamma > 0.f) {
392         // Round gamma to the nearest hundredth to prevent stupid
393         // precision choices and make it easier for apps to make
394         // decisions based on known gamma values. For example, you want
395         // 2.2, not 2.19998.
396         gamma = roundf(100.0 * gamma) / 100.0f;
397         if (gamma == 1.f)
398             m_spec.attribute("oiio:ColorSpace", "Linear");
399         else {
400             m_spec.attribute("oiio:ColorSpace",
401                              Strutil::sprintf("GammaCorrected%.2g", gamma));
402             m_spec.attribute("oiio:Gamma", gamma);
403         }
404     }
405 
406     float aspect = Strutil::stof(m_rla.AspectRatio);
407     if (aspect > 0.f)
408         m_spec.attribute("PixelAspectRatio", aspect);
409 
410     float f[3];  // variable will be reused for chroma, thus the array
411     // read chromaticity points
412     if (m_rla.RedChroma[0]) {
413         int num = sscanf(m_rla.RedChroma, "%f %f %f", f + 0, f + 1, f + 2);
414         if (num >= 2)
415             m_spec.attribute("rla:RedChroma",
416                              TypeDesc(TypeDesc::FLOAT,
417                                       num == 2 ? TypeDesc::VEC2 : TypeDesc::VEC3,
418                                       TypeDesc::POINT),
419                              f);
420     }
421     if (m_rla.GreenChroma[0]) {
422         int num = sscanf(m_rla.GreenChroma, "%f %f %f", f + 0, f + 1, f + 2);
423         if (num >= 2)
424             m_spec.attribute("rla:GreenChroma",
425                              TypeDesc(TypeDesc::FLOAT,
426                                       num == 2 ? TypeDesc::VEC2 : TypeDesc::VEC3,
427                                       TypeDesc::POINT),
428                              f);
429     }
430     if (m_rla.BlueChroma[0]) {
431         int num = sscanf(m_rla.BlueChroma, "%f %f %f", f + 0, f + 1, f + 2);
432         if (num >= 2)
433             m_spec.attribute("rla:BlueChroma",
434                              TypeDesc(TypeDesc::FLOAT,
435                                       num == 2 ? TypeDesc::VEC2 : TypeDesc::VEC3,
436                                       TypeDesc::POINT),
437                              f);
438     }
439     if (m_rla.WhitePoint[0]) {
440         int num = sscanf(m_rla.WhitePoint, "%f %f %f", f + 0, f + 1, f + 2);
441         if (num >= 2)
442             m_spec.attribute("rla:WhitePoint",
443                              TypeDesc(TypeDesc::FLOAT,
444                                       num == 2 ? TypeDesc::VEC2 : TypeDesc::VEC3,
445                                       TypeDesc::POINT),
446                              f);
447     }
448 
449     m_subimage = subimage;
450 
451     // N.B. the file pointer is now immediately after the scanline
452     // offset table for this subimage.
453     return true;
454 }
455 
456 
457 
458 bool
close()459 RLAInput::close()
460 {
461     if (m_file) {
462         fclose(m_file);
463         m_file = NULL;
464     }
465 
466     init();  // Reset to initial state
467     return true;
468 }
469 
470 
471 
472 size_t
decode_rle_span(unsigned char * buf,int n,int stride,const char * encoded,size_t elen)473 RLAInput::decode_rle_span(unsigned char* buf, int n, int stride,
474                           const char* encoded, size_t elen)
475 {
476     size_t e = 0;
477     while (n > 0 && e < elen) {
478         signed char count = (signed char)encoded[e++];
479         if (count >= 0) {
480             // run count positive: value repeated count+1 times
481             for (int i = 0; i <= count && n; ++i, buf += stride, --n)
482                 *buf = encoded[e];
483             ++e;
484         } else {
485             // run count negative: repeat bytes literally
486             count = -count;  // make it positive
487             for (; count && n > 0 && e < elen; --count, buf += stride, --n)
488                 *buf = encoded[e++];
489         }
490     }
491     if (n != 0) {
492         errorf("Read error: malformed RLE record");
493         return 0;
494     }
495     return e;
496 }
497 
498 
499 
500 bool
decode_channel_group(int first_channel,short num_channels,short num_bits,int)501 RLAInput::decode_channel_group(int first_channel, short num_channels,
502                                short num_bits, int /*y*/)
503 {
504     // Some preliminaries -- figure out various sizes and offsets
505     int chsize;         // size of the channels in this group, in bytes
506     int offset;         // buffer offset to first channel
507     int pixelsize;      // spacing between pixels (in bytes) in the output
508     TypeDesc chantype;  // data type for the channel
509     if (!m_spec.channelformats.size()) {
510         // No per-channel formats, they are all the same, so it's easy
511         chantype  = m_spec.format;
512         chsize    = chantype.size();
513         offset    = first_channel * chsize;
514         pixelsize = chsize * m_spec.nchannels;
515     } else {
516         // Per-channel formats differ, need to sum them up
517         chantype  = m_spec.channelformats[first_channel];
518         chsize    = chantype.size();
519         offset    = 0;
520         pixelsize = m_spec.pixel_bytes(true);
521         for (int i = 0; i < first_channel; ++i)
522             offset += m_spec.channelformats[i].size();
523     }
524 
525     // Read the big-endian values into the buffer.
526     // The channels are simply concatenated together in order.
527     // Each channel starts with a length, from which we know how many
528     // bytes of encoded RLE data to read.  Then there are RLE
529     // spans for each 8-bit slice of the channel.
530     std::vector<char> encoded;
531     for (int c = 0; c < num_channels; ++c) {
532         // Read the length
533         uint16_t length;  // number of encoded bytes
534         if (!read(&length)) {
535             errorf("Read error: couldn't read RLE record length");
536             return false;
537         }
538         // Read the encoded RLE record
539         encoded.resize(length);
540         if (!read(&encoded[0], length)) {
541             errorf("Read error: couldn't read RLE data span");
542             return false;
543         }
544 
545         if (chantype == TypeDesc::FLOAT) {
546             // Special case -- float data is just dumped raw, no RLE
547             for (int x = 0; x < m_spec.width; ++x)
548                 *((float*)&m_buf[offset + c * chsize + x * pixelsize])
549                     = ((float*)&encoded[0])[x];
550             continue;
551         }
552 
553         // Decode RLE -- one pass for each significant byte of the file,
554         // which we re-interleave properly by passing the right offsets
555         // and strides to decode_rle_span.
556         size_t eoffset = 0;
557         for (int bytes = 0; bytes < chsize; ++bytes) {
558             size_t e = decode_rle_span(&m_buf[offset + c * chsize + bytes],
559                                        m_spec.width, pixelsize,
560                                        &encoded[eoffset], length);
561             if (!e)
562                 return false;
563             eoffset += e;
564         }
565     }
566 
567     // If we're little endian, swap endianness in place for 2- and
568     // 4-byte pixel data.
569     if (littleendian()) {
570         if (chsize == 2) {
571             if (num_channels == m_spec.nchannels)
572                 swap_endian((uint16_t*)&m_buf[0], num_channels * m_spec.width);
573             else
574                 for (int x = 0; x < m_spec.width; ++x)
575                     swap_endian((uint16_t*)&m_buf[offset + x * pixelsize],
576                                 num_channels);
577         } else if (chsize == 4 && chantype != TypeDesc::FLOAT) {
578             if (num_channels == m_spec.nchannels)
579                 swap_endian((uint32_t*)&m_buf[0], num_channels * m_spec.width);
580             else
581                 for (int x = 0; x < m_spec.width; ++x)
582                     swap_endian((uint32_t*)&m_buf[offset + x * pixelsize],
583                                 num_channels);
584         }
585     }
586 
587     // If not 8*2^n bits, need to rescale.  For example, if num_bits is
588     // 10, the data values run 0-1023, but are stored in uint16.  So we
589     // now rescale to the full range of the output buffer range, per
590     // OIIO conventions.
591     if (num_bits == 8 || num_bits == 16 || num_bits == 32) {
592         // ok -- no rescaling needed
593     } else if (num_bits == 10) {
594         // fast, common case -- use templated hard-code
595         for (int x = 0; x < m_spec.width; ++x) {
596             uint16_t* b = (uint16_t*)(&m_buf[offset + x * pixelsize]);
597             for (int c = 0; c < num_channels; ++c)
598                 b[c] = bit_range_convert<10, 16>(b[c]);
599         }
600     } else if (num_bits < 8) {
601         // rare case, use slow code to make this clause short and simple
602         for (int x = 0; x < m_spec.width; ++x) {
603             uint8_t* b = (uint8_t*)&m_buf[offset + x * pixelsize];
604             for (int c = 0; c < num_channels; ++c)
605                 b[c] = bit_range_convert(b[c], num_bits, 8);
606         }
607     } else if (num_bits > 8 && num_bits < 16) {
608         // rare case, use slow code to make this clause short and simple
609         for (int x = 0; x < m_spec.width; ++x) {
610             uint16_t* b = (uint16_t*)&m_buf[offset + x * pixelsize];
611             for (int c = 0; c < num_channels; ++c)
612                 b[c] = bit_range_convert(b[c], num_bits, 16);
613         }
614     } else if (num_bits > 16 && num_bits < 32) {
615         // rare case, use slow code to make this clause short and simple
616         for (int x = 0; x < m_spec.width; ++x) {
617             uint32_t* b = (uint32_t*)&m_buf[offset + x * pixelsize];
618             for (int c = 0; c < num_channels; ++c)
619                 b[c] = bit_range_convert(b[c], num_bits, 32);
620         }
621     }
622     return true;
623 }
624 
625 
626 
627 bool
read_native_scanline(int subimage,int miplevel,int y,int,void * data)628 RLAInput::read_native_scanline(int subimage, int miplevel, int y, int /*z*/,
629                                void* data)
630 {
631     lock_guard lock(m_mutex);
632     if (!seek_subimage(subimage, miplevel))
633         return false;
634 
635     // By convention, RLA images store their images bottom-to-top.
636     y = m_spec.height - (y - m_spec.y) - 1;
637 
638     // Seek to scanline start, based on the scanline offset table
639     fseek(m_file, m_sot[y], SEEK_SET);
640 
641     // Now decode and interleave the channels.
642     // The channels are non-interleaved (i.e. rrrrrgggggbbbbb...).
643     // Color first, then matte, then auxiliary channels.  We can't
644     // decode all in one shot, though, because the data type and number
645     // of significant bits may be may be different for each class of
646     // channels, so we deal with them separately and interleave into
647     // our buffer as we go.
648     size_t size = m_spec.scanline_bytes(true);
649     m_buf.resize(size);
650     if (m_rla.NumOfColorChannels > 0)
651         if (!decode_channel_group(0, m_rla.NumOfColorChannels,
652                                   m_rla.NumOfChannelBits, y))
653             return false;
654     if (m_rla.NumOfMatteChannels > 0)
655         if (!decode_channel_group(m_rla.NumOfColorChannels,
656                                   m_rla.NumOfMatteChannels,
657                                   m_rla.NumOfMatteBits, y))
658             return false;
659     if (m_rla.NumOfAuxChannels > 0)
660         if (!decode_channel_group(m_rla.NumOfColorChannels
661                                       + m_rla.NumOfMatteChannels,
662                                   m_rla.NumOfAuxChannels, m_rla.NumOfAuxBits,
663                                   y))
664             return false;
665 
666     memcpy(data, &m_buf[0], size);
667     return true;
668 }
669 
670 
671 
672 inline int
get_month_number(const char * s)673 RLAInput::get_month_number(const char* s)
674 {
675     static const char* months[] = { "",    "jan", "feb", "mar", "apr",
676                                     "may", "jun", "jul", "aug", "sep",
677                                     "oct", "nov", "dec" };
678     for (int i = 1; i <= 12; ++i)
679         if (Strutil::iequals(s, months[i]))
680             return i;
681     return -1;
682 }
683 
684 
685 
686 inline TypeDesc
get_channel_typedesc(short chan_type,short chan_bits)687 RLAInput::get_channel_typedesc(short chan_type, short chan_bits)
688 {
689     switch (chan_type) {
690     case CT_BYTE:
691         // some non-spec-compliant images > 8bpc will have it set to
692         // byte anyway, so try guessing by bit depth instead
693         if (chan_bits > 8) {
694             switch ((chan_bits + 7) / 8) {
695             case 2: return TypeDesc::UINT16;
696             case 3:
697             case 4: return TypeDesc::UINT32;
698             default: OIIO_ASSERT(!"Invalid colour channel type");
699             }
700         } else
701             return TypeDesc::UINT8;
702     case CT_WORD: return TypeDesc::UINT16;
703     case CT_DWORD: return TypeDesc::UINT32;
704     case CT_FLOAT: return TypeDesc::FLOAT;
705     default: OIIO_ASSERT(!"Invalid colour channel type");
706     }
707     // shut up compiler
708     return TypeDesc::UINT8;
709 }
710 
711 OIIO_PLUGIN_NAMESPACE_END
712