Blame converter/other/rlatopam.c

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