about summary refs log tree commit diff
path: root/converter/other/rlatopam.c
diff options
context:
space:
mode:
Diffstat (limited to 'converter/other/rlatopam.c')
-rw-r--r--converter/other/rlatopam.c433
1 files changed, 433 insertions, 0 deletions
diff --git a/converter/other/rlatopam.c b/converter/other/rlatopam.c
new file mode 100644
index 00000000..ae1326ca
--- /dev/null
+++ b/converter/other/rlatopam.c
@@ -0,0 +1,433 @@
+/** rlatopam.c - read Alias/Wavefront RLA or RPF file
+ **
+ ** Copyright (C) 2005 Matte World Digital
+ **
+ ** Author: Simon Walton
+ **
+ ** Permission to use, copy, modify, and distribute this software and its
+ ** documentation for any purpose and without fee is hereby granted, provided
+ ** that the above copyright notice appear in all copies and that both that
+ ** copyright notice and this permission notice appear in supporting
+ ** documentation.  This software is provided "as is" without express or
+ ** implied warranty.
+ **/
+
+
+#include <string.h>
+#include <errno.h>
+
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "pam.h"
+#include "rla.h"
+
+static int * offsets;
+static bool is_float;
+static bool has_matte;
+
+static unsigned int depth;
+static unsigned int width;
+static unsigned int height;
+static unsigned int chanBits;
+static short storageType;
+static struct pam outpam;
+static unsigned int numChan;
+
+
+static void
+parseCommandLine(int           const argc,
+                 char **       const argv,
+                 const char ** const inputFileNameP) {
+
+    if (argc-1 < 1)
+        *inputFileNameP = "-";
+    else {
+        *inputFileNameP = argv[1];
+
+        if (argc-1 > 1)
+            pm_error("There is at most one argument - input file name.  "
+                     "You specified %u", argc-1);
+    }
+}
+
+
+
+static bool littleEndian;
+
+
+static void
+determineEndianness(void) {
+
+    union {
+        unsigned char bytes[2];
+        unsigned short number;
+    } u;
+
+    u.number = 1;
+
+    littleEndian = (u.bytes[0] == 1);
+}
+
+
+
+static unsigned short
+byteswap(unsigned short const input) {
+
+    return (input << 8) | (input >> 8);
+}
+
+
+
+static void
+read_header(FILE *   const ifP,
+            rlahdr * const hdrP) {
+
+    rlahdr hdr;
+    size_t bytesRead;
+    
+    fseek (ifP, 0, SEEK_SET);
+    
+    /* Here we have a hack.  The bytes in the file are almost in the
+       same format as the compiler stores 'hdr' in memory.  The only
+       difference is that the compiler may store the integer values
+       in the obscene little-endian format.  So we just read the whole
+       header into 'hdr' as if it were the right format, and then
+       correct the integer values by swapping their bytes.
+
+       The _right_ way to do this is to read the file one field at a time,
+       using pm_readbigshort() where appropriate.
+    */
+
+    bytesRead = fread(&hdr, sizeof hdr, 1, ifP);
+    if (bytesRead != 1)
+        pm_error("Unexpected EOF on input file.");
+
+    if (littleEndian) {
+        hdr.window.left          = byteswap(hdr.window.left);
+        hdr.window.right         = byteswap(hdr.window.right);
+        hdr.window.bottom        = byteswap(hdr.window.bottom);
+        hdr.window.top           = byteswap(hdr.window.top);
+        hdr.active_window.left   = byteswap(hdr.active_window.left);
+        hdr.active_window.right  = byteswap(hdr.active_window.right);
+        hdr.active_window.bottom = byteswap(hdr.active_window.bottom);
+        hdr.active_window.top    = byteswap(hdr.active_window.top);
+        hdr.storage_type         = byteswap(hdr.storage_type);
+        hdr.num_chan             = byteswap(hdr.num_chan);
+        hdr.num_matte            = byteswap(hdr.num_matte);
+        hdr.revision             = byteswap(hdr.revision);
+        hdr.chan_bits            = byteswap(hdr.chan_bits);
+        hdr.matte_type           = byteswap(hdr.matte_type);
+        hdr.matte_bits           = byteswap(hdr.matte_bits);
+    }
+
+    if (hdr.revision != 0xfffe && hdr.revision != 0xfffd)
+        pm_error("Invalid file header.  \"revision\" field should contain "
+                 "0xfffe or 0xfffd, but contains 0x%04x", hdr.revision);
+
+    *hdrP = hdr;
+}
+
+
+
+static void
+decodeFP(unsigned char * const in,
+         unsigned char * const out,
+         int             const width,
+         int             const stride) {
+
+    unsigned int x;
+    unsigned char * inputCursor;
+    unsigned char * outputCursor;
+
+    inputCursor = &in[0];
+
+    for (x = 0; x < width; ++x) {
+        union {char bytes [4]; float fv;} fi;
+        unsigned short val;
+
+        if (littleEndian) {
+            fi.bytes [3] = *inputCursor++;
+            fi.bytes [2] = *inputCursor++;
+            fi.bytes [1] = *inputCursor++;
+            fi.bytes [0] = *inputCursor++;
+        } else {
+            fi.bytes [0] = *inputCursor++;
+            fi.bytes [1] = *inputCursor++;
+            fi.bytes [2] = *inputCursor++;
+            fi.bytes [3] = *inputCursor++;
+        }
+
+        val = fi.fv > 1 ? 65535 : (fi.fv < 0 ? 0 :
+                                   (unsigned short) (65535 * fi.fv + .5));
+        outputCursor[0] = val >> 8; outputCursor[1] = val & 0xff;
+        outputCursor += stride;
+    }
+}
+
+
+
+static unsigned char *
+decode(unsigned char * const input,
+       unsigned char * const output,
+       int             const xFile,
+       int             const xImage,
+       int             const stride) {
+
+    int x;
+    unsigned int bytes;
+    unsigned int useX;
+    unsigned char * inputCursor;
+    unsigned char * outputCursor;
+
+    inputCursor = &input[0];
+    outputCursor = &output[0];
+    x = xFile;
+    bytes = 0;
+    useX = 0;
+    
+    while (x > 0) {
+        int count;
+
+        count = *(signed char *)inputCursor++;
+        ++bytes;
+
+        if (count >= 0) {
+            /* Repeat pixel value (count + 1) times. */
+            while (count-- >= 0) {
+                if (useX < xImage) {
+                    *outputCursor = *inputCursor;
+                    outputCursor += stride;
+                }
+                --x;
+                ++useX;
+            }
+            ++inputCursor;
+            ++bytes;
+        } else {
+            /* Copy (-count) unencoded values. */
+            for (count = -count; count > 0; --count) {
+                if (useX < xImage) {
+                    *outputCursor = *inputCursor;
+                    outputCursor += stride;
+                }
+                ++inputCursor;
+                ++bytes;
+                --x;
+                ++useX;
+            }
+        }
+    }
+    return inputCursor;
+}
+
+
+
+static void
+decode_row(FILE *          const ifP,
+           int             const row,
+           unsigned char * const rb) {
+
+    static unsigned char * read_buffer = NULL;
+    unsigned int chan;
+    int rc;
+
+    if (!read_buffer) {
+        MALLOCARRAY(read_buffer, width * 4);
+        if (read_buffer == 0)
+            pm_error("Unable to get memory for read_buffer");
+    }
+
+    rc = fseek (ifP, offsets [height - row - 1], SEEK_SET);
+    if (rc != 0)
+        pm_error("fseek() failed with errno %d (%s)",
+                 errno, strerror(errno));
+
+    for (chan = 0; chan < outpam.depth; ++chan) {
+        unsigned short length;
+        size_t bytesRead;
+        
+        pm_readbigshortu(ifP, &length);
+        if (length > width * 4)
+            pm_error("Line too long - row %u, channel %u", row, chan);
+
+        bytesRead = fread(read_buffer, 1, length, ifP);
+        if (bytesRead != length)
+            pm_error("EOF encountered unexpectedly");
+
+        if (is_float)
+            decodeFP(read_buffer, rb + chan * 2, width, outpam.depth * 2);
+        else if (depth > 8) {
+            /* Hi byte */
+            unsigned char * const newpos =
+                decode(read_buffer, rb + chan * 2, width, width,
+                       outpam.depth * 2);
+            /* Lo byte */
+            decode(newpos, rb + chan * 2 + 1, width, width,
+                   outpam.depth * 2);
+        } else
+            decode(read_buffer, rb + chan, width, width, outpam.depth); 
+    }
+}
+
+
+
+static void
+getHeaderInfo(FILE *         const ifP,
+              unsigned int * const widthP,
+              unsigned int * const heightP,
+              unsigned int * const depthP,
+              bool *         const hasMatteP,
+              unsigned int * const chanBitsP,
+              short *        const storageType) {
+    
+    rlahdr hdr;
+    int width, height;
+
+    read_header(ifP, &hdr);
+
+    height = hdr.active_window.top - hdr.active_window.bottom + 1;
+    width  = hdr.active_window.right - hdr.active_window.left + 1;
+    if (width <=0)
+        pm_error("Invalid input image.  It says its width isn't positive");
+    if (height <=0)
+        pm_error("Invalid input image.  It says its height isn't positive");
+
+    *widthP  = width;
+    *heightP = height;
+
+    if (hdr.num_chan != 1 && hdr.num_chan != 3)
+        pm_error ("Input image has bad number of channels: %d.  "
+                  "Should be 1 or 3.",
+                  hdr.num_chan);
+
+    *depthP = hdr.chan_bits <= 8 ? 8 : 16;
+
+    *hasMatteP = (hdr.num_matte > 0);
+    *chanBitsP = hdr.chan_bits;
+}
+
+
+
+static void
+readOffsetArray(FILE *       const ifP,
+                int **       const offsetsP,
+                unsigned int const height) {
+
+    int * offsets;
+    unsigned int row;
+
+    MALLOCARRAY(offsets, height);
+    if (offsets == NULL)
+        pm_error("Unable to allocate memory for the offsets array");
+
+    for (row = 0; row < height; ++row) {
+        long l;
+        pm_readbiglong(ifP, &l);
+        offsets[row] = l;
+    }
+    *offsetsP = offsets;
+}
+
+
+
+static void
+destroyOffsetArray(int * const offsets) {
+
+    free(offsets);
+}
+
+
+
+static void
+readAndWriteRaster(FILE *             const ifP,
+                   const struct pam * const outpamP) {
+
+    unsigned char * rowBuffer;
+    tuple * tuplerow;
+    unsigned int row;
+
+    /* Hold one row of all image planes */
+    rowBuffer = calloc(1, width * outpamP->depth * 4);
+    if (rowBuffer == NULL)
+        pm_error("Unable to allocate memor for row buffer.");
+
+    tuplerow = pnm_allocpamrow(outpamP);
+
+    for (row = 0; row < height; ++row) {
+        unsigned int col;
+        unsigned char * rbP;
+
+        decode_row(ifP, row, rowBuffer);
+        for (col = 0, rbP = rowBuffer; col < width; ++col) {
+            unsigned int chan;
+            for (chan = 0; chan < outpamP->depth; ++chan) {
+                if (depth > 8) {
+                    tuplerow[col][chan] = 256 * rbP[0] + rbP[1];
+                    rbP += 2;
+                } else {
+                    tuplerow[col][chan] = *rbP;
+                    rbP += 1;
+                }
+            }
+        }
+        pnm_writepamrow(outpamP, tuplerow);
+    }
+    pnm_freepamrow(tuplerow);
+    free(rowBuffer);
+}
+
+
+
+int
+main(int    argc,
+     char * argv[]) {
+
+    const char * inputFileName;
+    FILE * ifP;
+
+    pnm_init(&argc, argv);
+
+    determineEndianness();
+
+    parseCommandLine(argc, argv, &inputFileName);
+
+    ifP = pm_openr_seekable(inputFileName);
+
+    getHeaderInfo(ifP, &width, &height, &depth, &has_matte,
+                  &chanBits, &storageType);
+
+    outpam.size   = outpam.len = sizeof (struct pam);
+    outpam.file   = stdout;
+    outpam.format = PAM_FORMAT;
+    outpam.height = height;
+    outpam.width  = width;
+    outpam.depth  = numChan + (has_matte ? 1 : 0);
+    outpam.maxval = (1 << (chanBits > 16 ? 
+                           (9 + (chanBits - 1) % 8)
+                                /* Take top 2 of 3 or 4 bytes */
+                           : chanBits)) - 1;
+
+    /* Most apps seem to assume 32 bit integer is really floating point */
+    if (chanBits == 32 || storageType == 4) {
+        is_float = TRUE;
+        outpam.maxval = 65535;
+        depth = 16;
+    }
+
+    outpam.bytes_per_sample = depth / 8;
+    strcpy(outpam.tuple_type, (numChan == 3 ? "RGB" : "GRAYSCALE"));
+    if (has_matte)
+        strcat(outpam.tuple_type, "A");
+
+    readOffsetArray(ifP, &offsets, height);
+
+    pnm_writepaminit(&outpam);
+
+    readAndWriteRaster(ifP, &outpam);
+
+    destroyOffsetArray(offsets);
+    
+    pm_close(ifP);
+
+    return 0; 
+}