cnv/rletoabA62/rletoabA62.c
/*
* 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);
}
}
}