cnv/rletoabA62/rletoabA62.c

Summary

Maintainability
Test Coverage
/*
 * Copyright (C) 1988 Research Institute for Advanced Computer Science.
 * All rights reserved.  The RIACS Software Policy contains specific
 * terms and conditions on the use of this software, and must be
 * distributed with any copies.  This file may be redistributed.  This
 * copyright and notice must be preserved in all copies made of this file.
 */

/*
 * rletoabA62
 * ----------
 *
 * This program converts Utah Raster Toolkit files into the dump format of the
 * Abekas A62 video storage disk.
 *
 * Options:
 *    -N ... do no digital filtering.
 *    -n N ... N specifies the number for frames to write
 *    -f N ... N specifies the first frame number (1-4) to write.
 *    -b R G B ... clear background to this color
 *
 * Author/Status:
 *    Bob Brown        rlb@riacs.edu
 *    First write: 29 Jan 1988
 *
 * The interface to the particular raster format used is mostly separated from
 * the logic of the conversion code.  Two routines define the interface:
 *
 *    rasterInit(fd, width, height)
 *
 *        This specifies the integer file descriptor for the input file,
 *        the width and the height of the image.  It is called exactly once.
 *
 *    rasterGetRow(red, green, blue)
 *
 *        This is called to return the next row of the raster. The parameters
 *        point to arrays of unsigned char to hold the RGB values.
 */
static char rcsid[] = "$Header: /l/spencer/src/urt/cnv/rletoabA62/RCS/rletoabA62.c,v 3.0.1.1 1992/04/30 14:15:19 spencer Exp $";
/*
rletoabA62()                Tag the file.
*/

#include <stdio.h>

#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
typedef char bool;

/*
 * Dimensions of the raster image within the A62
 */

#define WIDTH 768
#define HEIGHT 512

/*
 * Global variables are always capitalized.
 *
 * These are globals set by program arguments.  The static initializations are
 * the default values.
 */

int        Width = WIDTH;        /* Width of the output image */
int        Height = HEIGHT;    /* Height of the output image */
int        Debug = 0;
bool        NoFilter = FALSE;
int        Frame = 1;
int        NumFrames = 2;
int        BkgRed = 0;
int        BkgBlue = 0;
int        BkgGreen= 0;

extern char    *optarg;        /* interface to getopt() */
extern int    optind;        /* interface to getopt() */

typedef struct {
    char            y, i, q;
} yiq_t;

#ifdef USE_PROTOTYPES
extern void rasterInit(int fd, int width, int height);
extern void rasterRowGet(unsigned char *red, unsigned char *green, unsigned char *blue);
extern void rasterDone(void);
extern void filterY(float *yVal, float c0, float c1, float c2);
extern void filterIQ(float *iqVal, float c0, float c1, float c2, int start, int stride);
extern void dump1(register yiq_t **raster, int start);
extern void dump2(register yiq_t **raster, int start);
#else
extern void rasterInit();
extern void rasterRowGet(), rasterDone();
extern void filterY();
extern void filterIQ();
extern void dump1();
extern void dump2();
#endif

/*
 * Main entry...
 */

void
main(argc,argv)
int argc;
char **argv;
{
    register int    i, j;
    int             errors, c, file;
    float           rTOy[256], gTOy[256], bTOy[256];
    float           rTOi[256], gTOi[256], bTOi[256];
    float           rTOq[256], gTOq[256], bTOq[256];
    float          *iVal, *yVal, *qVal, *tmpVal;
    unsigned char  *red, *green, *blue;
    int             r, g, b;
    yiq_t         **raster;

    /*
     * Parse program arguments... 
     *
     * The -w and -h args should actually not be used. 
     */

    errors = 0;
    while ((c = getopt(argc, argv, "Nn:f:w:h:d:D:b")) != EOF) {
    switch (c) {
    case 'b':
        BkgRed = atoi(argv[optind++]);
        BkgGreen = atoi(argv[optind++]);
        BkgBlue = atoi(argv[optind++]);
        break;
    case 'N':
        NoFilter = TRUE;
        break;
    case 'w':
        Width = atoi(optarg);
        break;
    case 'f':
        Frame = atoi(optarg);
        break;
    case 'n':
        NumFrames = atoi(optarg);
        break;
    case 'h':
        Height = atoi(optarg);
        break;
    case 'D':
        Debug = atoi(optarg);
        break;
    case '?':
        errors++;
    }
    }
    if (errors > 0) {
    fprintf(stderr, "Usage: %s [-n] [-D debug-level] [file]\n", argv[0]);
    exit(1);
    }
    if (optind < argc) {
    if ((file = open(argv[optind], 0)) == -1) {
        perror(argv[optind]);
        exit(1);
    }
    } else {
    file = 0;
    }

    /*
     * Initialize the type manager for Utah RLE files 
     */

    rasterInit(file, Width, Height, BkgRed, BkgGreen, BkgBlue);

    /*
     * Allocate storage for the RGB inputs, the computed YIQ, and the
     * results. This is all dynamically allocated because the dimensions of
     * the framestore are only decided at run time. 
     *
     * The YIQ arrays are extended two entries at either end to simplify the
     * filtering code. The effect is that the iVal and qVal arrays can be
     * indexed [-2 .. Width+2] and yVal [-4 .. Width+4] (the stuff at the
     * end of the yVal array isn't used). 
     */

    red = (unsigned char *) calloc(Width, sizeof *red);
    green = (unsigned char *) calloc(Width, sizeof *green);
    blue = (unsigned char *) calloc(Width, sizeof *blue);

    tmpVal = (float *) calloc(Width + 8, sizeof *yVal);
    yVal = tmpVal + 4;
    tmpVal = (float *) calloc(Width + 4, sizeof *iVal);
    iVal = tmpVal + 2;
    tmpVal = (float *) calloc(Width + 4, sizeof *qVal);
    qVal = tmpVal + 2;

    /*
     * Allocate storage to hold the 8-bit YIQ values for the entire
     * picture. 
     */

    raster = (yiq_t **) calloc(Height, sizeof *raster);
    for (i = 0; i < Height; i++) {
    raster[i] = (yiq_t *) calloc(Width, sizeof **raster);
    }

    /*
     * Build the mappings from R,G,B to Y,I,Q. The pedastal is factored
     * into this mapping. 
     */

    for (i = 0; i < 256; i++) {
    rTOy[i] = ((float) i / 255.0 * 0.925 + 0.075) * 0.299;
    gTOy[i] = ((float) i / 255.0 * 0.925 + 0.075) * 0.587;
    bTOy[i] = ((float) i / 255.0 * 0.925 + 0.075) * 0.114;

    rTOi[i] = ((float) i / 255.0 * 0.925 + 0.075) * 0.596;
    gTOi[i] = ((float) i / 255.0 * 0.925 + 0.075) * -0.274;
    bTOi[i] = ((float) i / 255.0 * 0.925 + 0.075) * -0.322;

    rTOq[i] = ((float) i / 255.0 * 0.925 + 0.075) * 0.211;
    gTOq[i] = ((float) i / 255.0 * 0.925 + 0.075) * -0.523;
    bTOq[i] = ((float) i / 255.0 * 0.925 + 0.075) * 0.312;
    }

    /*
     * process the input raster line by raster line 
     */

    for (i = 0; i < Height; i++) {
    rasterRowGet(red, green, blue);
    yVal[-4] = yVal[-3] = yVal[-2] = yVal[-1] = 0;
    iVal[-2] = iVal[-1] = 0;
    qVal[-2] = qVal[-1] = 0;
    yVal[Width + 3] = yVal[Width + 2] = yVal[Width + 1] = yVal[Width + 0] = 0;
    iVal[Width + 0] = iVal[Width + 1] = 0;
    qVal[Width + 0] = qVal[Width + 1] = 0;

    /*
     * Convert RGB to YIQ.  The multiplication is done by table lookup
     * into the [rgb]TO[yiq] arrays. 
     */

    for (j = 0; j < Width; j++) {
        r = red[j];
        g = green[j];
        b = blue[j];
        yVal[j] = rTOy[r] + gTOy[g] + bTOy[b];
        iVal[j] = rTOi[r] + gTOi[g] + bTOi[b];
        qVal[j] = rTOq[r] + gTOq[g] + bTOq[b];
    }
    if (!NoFilter) {
        filterY(yVal, 0.62232, 0.21732, -0.02848);
        filterIQ(iVal, 0.42354, 0.25308, 0.03515, 0, 2);
        filterIQ(qVal, 0.34594, 0.25122, 0.07581, 1, 2);
    }
    /*
     * Build the YIQ raster 
     */

    for (j = 0; j < Width; j++) {
        raster[Height - i - 1][j].y = yVal[j] * 140.0;
        raster[Height - i - 1][j].i = iVal[j] * 140.0;
        raster[Height - i - 1][j].q = qVal[j] * 140.0;
    }
    }

    /*
     * Dump the raster as four color fields. 
     *
     * Assert that Width%4 ==0 and Height%4 == 0 
     *
     * Despite what the A62 SCSI manual says, for frames I and IV, the even
     * numbered lines (starting with line 0) begin Y-I and the others begin
     * Y+I. 
     */

    for (i = 0; i < NumFrames; i++) {
    switch ((i + Frame - 1) % 4 + 1) {
    case 1:
        dump2(raster, 0);    /* even lines, starts Y-I */
        break;
    case 2:
        dump1(raster, 1);    /* odd lines, starts Y+I */
        break;
    case 3:
        dump1(raster, 0);    /* even lines, starts Y+I */
        break;
    case 4:
        dump2(raster, 1);    /* odd lines, starts Y-I */
        break;
    }
    }
    exit(0);
}

/*
 * filterY
 * -------
 *
 * Apply and RIF filter to the luminance data. The signal is shifted two pixels
 * to the right in the process.
 * 
 * The multiplier arrays m0, m1, and m2 exist to reduce the number of floating
 * point multiplications and to allow the filtering to occur in place.
 */
void
filterY(yVal, c0, c1, c2)
float *yVal, c0, c1, c2;
{
    static float   *m0 = NULL;
    static float   *m1 = NULL;
    static float   *m2 = NULL;
    register int    i;

    if (m1 == NULL) {
    m0 = (float *) calloc(Width + 8, sizeof(float));
    m1 = (float *) calloc(Width + 8, sizeof(float));
    m2 = (float *) calloc(Width + 8, sizeof(float));
    }
    for (i = -4; i < Width + 4; i++) {
    m0[i] = c0 * yVal[i];
    m1[i] = c1 * yVal[i];
    m2[i] = c2 * yVal[i];
    }
    for (i = 0; i < Width; i++) {
    yVal[i] = m2[i - 4] + m1[i - 3] + m0[i - 2] + m1[i - 1] + m2[i];
    }
}
/*
 * filterIQ
 * --------
 *
 * Apply and RIF filter to the color difference data.
 *
 * The multiplier arrays m0, m1, and m2 exist to reduce the number of floating
 * point multiplications and to allow the filtering to occur in place.
 *
 * The "start" and "stride" parameters are used to reduce the number of
 * computations because I and Q are only used every other pixel.
 *
 * This is different from the manual in than the filtering is done on adjacent
 * pixels, rather than every other pixel.  This may be a problem...
 */
void 
filterIQ(iqVal, c0, c1, c2, start, stride)
float *iqVal, c0, c1, c2;
int start, stride;
{
    static float   *m0 = NULL;
    static float   *m1 = NULL;
    static float   *m2 = NULL;
    register int    i;

    if (m1 == NULL) {
    m0 = (float *) calloc(Width + 4, sizeof(float));
    m1 = (float *) calloc(Width + 4, sizeof(float));
    m2 = (float *) calloc(Width + 4, sizeof(float));
    }
    for (i = -2; i < Width + 2; i++) {
    m0[i] = c0 * iqVal[i];
    m1[i] = c1 * iqVal[i];
    m2[i] = c2 * iqVal[i];
    }
    for (i = start; i < Width; i += stride) {
    iqVal[i] = m2[i - 2] + m1[i - 1] + m0[i] + m1[i + 1] + m2[i + 2];
    }
}

/*
 * dump1
 * -----
 *
 * Dumps the raster starting with the sequence Y+I Y+Q Y-I Y-Q
 *
 * This routine also adds 60 to put the data in the 60 .. 200 range.
 */

#define OUT(y, OP, iq) putc((char) (raster[i][j + 0].y OP raster[i][j + 0].iq + 60), stdout);
void
dump1(raster, start)
register yiq_t **raster;
int start;
{
    register int    i, j;

    for (i = start; i < Height; i += 4) {    /* field I */
    for (j = 0; j < Width; j += 4) {
        putc((char) (raster[i][j + 0].y + raster[i][j + 0].i + 60), stdout);
        putc((char) (raster[i][j + 1].y + raster[i][j + 1].q + 60), stdout);
        putc((char) (raster[i][j + 2].y - raster[i][j + 2].i + 60), stdout);
        putc((char) (raster[i][j + 3].y - raster[i][j + 3].q + 60), stdout);
    }
    for (j = 0; j < Width; j += 4) {
        putc((char) (raster[i + 2][j + 0].y - raster[i + 2][j + 0].i + 60), stdout);
        putc((char) (raster[i + 2][j + 1].y - raster[i + 2][j + 1].q + 60), stdout);
        putc((char) (raster[i + 2][j + 2].y + raster[i + 2][j + 2].i + 60), stdout);
        putc((char) (raster[i + 2][j + 3].y + raster[i + 2][j + 3].q + 60), stdout);
    }
    }
}

/*
 * dump2
 * -----
 *
 * Dumps the raster starting with the sequence Y-I Y-Q Y+I Y+Q
 *
 * This routine also adds 60 to put the data in the 60 .. 200 range.
 */

void
dump2(raster, start)
register yiq_t **raster;
int start;
{
    register int    i, j;

    for (i = start; i < Height; i += 4) {    /* field I */
    for (j = 0; j < Width; j += 4) {
        putc((char) (raster[i][j + 0].y - raster[i][j + 0].i + 60), stdout);
        putc((char) (raster[i][j + 1].y - raster[i][j + 1].q + 60), stdout);
        putc((char) (raster[i][j + 2].y + raster[i][j + 2].i + 60), stdout);
        putc((char) (raster[i][j + 3].y + raster[i][j + 3].q + 60), stdout);
    }
    for (j = 0; j < Width; j += 4) {
        putc((char) (raster[i + 2][j + 0].y + raster[i + 2][j + 0].i + 60), stdout);
        putc((char) (raster[i + 2][j + 1].y + raster[i + 2][j + 1].q + 60), stdout);
        putc((char) (raster[i + 2][j + 2].y - raster[i + 2][j + 2].i + 60), stdout);
        putc((char) (raster[i + 2][j + 3].y - raster[i + 2][j + 3].q + 60), stdout);
    }
    }
}