/*
* This software is copyrighted as noted below. It may be freely copied,
* modified, and redistributed, provided that the copyright notice is
* preserved on all copies.
*
* There is no warranty or other guarantee of fitness for this software,
* it is provided solely "as is". Bug reports or fixes may be sent
* to the author, who may or may not act on them as he desires.
*
* You may not include this software in a program or other software product
* without supplying the source, or without informing the end-user that the
* source is available for no extra charge.
*
* If you modify this software, you should include a notice giving the
* name of the person performing the modification, the date of modification,
* and the reason for such modification.
*
* Modified at BRL 16-May-88 by Mike Muuss to avoid Alliant STDC desire
* to have all "void" functions so declared.
*/
/*
* rle_getrow.c - Read an RLE file in.
*
* Author: Spencer W. Thomas
* Computer Science Dept.
* University of Utah
* Date: Wed Apr 10 1985
* Copyright (c) 1985 Spencer W. Thomas
*
* $Id: rle_getrow.c,v 3.0.1.5 1992/03/04 19:33:08 spencer Exp spencer $
*/
#include <string.h>
#include <stdio.h>
#include "netpbm/pm.h"
#include "netpbm/mallocvar.h"
#include "rle.h"
#include "rle_code.h"
#include "vaxshort.h"
/* Read a two-byte "short" that started in VAX (LITTLE_ENDIAN) order */
#define VAXSHORT( var, fp )\
{ var = fgetc(fp)&0xFF; var |= (fgetc(fp)) << 8; }
/* Instruction format -- first byte is opcode, second is datum. */
#define OPCODE(inst) (inst[0] & ~LONG)
#define LONGP(inst) (inst[0] & LONG)
#define DATUM(inst) (inst[1] & 0xff) /* Make sure it's unsigned. */
static int debug_f; /* If non-zero, print debug info. */
int
rle_get_setup(rle_hdr * const the_hdr) {
/*-----------------------------------------------------------------------------
Read the initialization information from an RLE file.
Inputs:
the_hdr: Contains pointer to the input file.
Outputs:
the_hdr: Initialized with information from the input file.
Returns
0 on success,
-1 if the file is not an RLE file,
-2 if malloc of the color map failed,
-3 if an immediate EOF is hit (empty input file)
-4 if an EOF is encountered reading the setup information.
Assumptions:
infile points to the "magic" number in an RLE file (usually byte 0
in the file).
Algorithm:
Read in the setup info and fill in the_hdr.
---------------------------------------------------------------------------- */
struct XtndRsetup setup;
short magic;
FILE * infile = the_hdr->rle_file;
int i;
char * comment_buf;
/* Clear old stuff out of the header. */
rle_hdr_clear(the_hdr);
if (the_hdr->is_init != RLE_INIT_MAGIC)
rle_names(the_hdr, "Urt", "some file", 0);
++the_hdr->img_num; /* Count images. */
VAXSHORT(magic, infile);
if (feof(infile))
return RLE_EMPTY;
if (magic != RLE_MAGIC)
return RLE_NOT_RLE;
fread(&setup, 1, SETUPSIZE, infile); /* assume VAX packing */
if (feof( infile))
return RLE_EOF;
/* Extract information from setup */
the_hdr->ncolors = setup.h_ncolors;
for (i = 0; i < the_hdr->ncolors; ++i)
RLE_SET_BIT(*the_hdr, i);
if (!(setup.h_flags & H_NO_BACKGROUND) && setup.h_ncolors > 0) {
rle_pixel * bg_color;
MALLOCARRAY(the_hdr->bg_color, setup.h_ncolors);
MALLOCARRAY(bg_color, 1 + (setup.h_ncolors / 2) * 2);
RLE_CHECK_ALLOC(the_hdr->cmd, the_hdr->bg_color && bg_color,
"background color" );
fread((char *)bg_color, 1, 1 + (setup.h_ncolors / 2) * 2, infile);
for (i = 0; i < setup.h_ncolors; ++i)
the_hdr->bg_color[i] = bg_color[i];
free(bg_color);
} else {
getc(infile); /* skip filler byte */
the_hdr->bg_color = NULL;
}
if (setup.h_flags & H_NO_BACKGROUND)
the_hdr->background = 0;
else if (setup.h_flags & H_CLEARFIRST)
the_hdr->background = 2;
else
the_hdr->background = 1;
if (setup.h_flags & H_ALPHA) {
the_hdr->alpha = 1;
RLE_SET_BIT( *the_hdr, RLE_ALPHA );
} else
the_hdr->alpha = 0;
the_hdr->xmin = vax_gshort(setup.hc_xpos);
the_hdr->ymin = vax_gshort(setup.hc_ypos);
the_hdr->xmax = the_hdr->xmin + vax_gshort(setup.hc_xlen) - 1;
the_hdr->ymax = the_hdr->ymin + vax_gshort(setup.hc_ylen) - 1;
the_hdr->ncmap = setup.h_ncmap;
the_hdr->cmaplen = setup.h_cmaplen;
if (the_hdr->ncmap > 0) {
int const maplen = the_hdr->ncmap * (1 << the_hdr->cmaplen);
int i;
char *maptemp;
MALLOCARRAY(the_hdr->cmap, maplen);
MALLOCARRAY(maptemp, 2 * maplen);
if (the_hdr->cmap == NULL || maptemp == NULL) {
pm_error("Malloc failed for color map of size %d*%d "
"in rle_get_setup, reading '%s'",
the_hdr->ncmap, (1 << the_hdr->cmaplen),
the_hdr->file_name );
return RLE_NO_SPACE;
}
fread(maptemp, 2, maplen, infile);
for (i = 0; i < maplen; ++i)
the_hdr->cmap[i] = vax_gshort(&maptemp[i * 2]);
free(maptemp);
}
/* Check for comments */
if (setup.h_flags & H_COMMENT) {
short comlen, evenlen;
char * cp;
VAXSHORT(comlen, infile); /* get comment length */
overflow_add(comlen, 1);
evenlen = (comlen + 1) & ~1; /* make it even */
if (evenlen) {
MALLOCARRAY(comment_buf, evenlen);
if (comment_buf == NULL) {
pm_error("Malloc failed for comment buffer of size %d "
"in rle_get_setup, reading '%s'",
comlen, the_hdr->file_name );
return RLE_NO_SPACE;
}
fread(comment_buf, 1, evenlen, infile);
/* Count the comments */
for (i = 0, cp = comment_buf; cp < comment_buf + comlen; ++cp)
if (*cp == '\0')
++i;
++i; /* extra for NULL pointer at end */
/* Get space to put pointers to comments */
MALLOCARRAY(the_hdr->comments, i);
if (the_hdr->comments == NULL) {
pm_error("Malloc failed for %d comment pointers "
"in rle_get_setup, reading '%s'",
i, the_hdr->file_name );
return RLE_NO_SPACE;
}
/* Get pointers to the comments */
*the_hdr->comments = comment_buf;
for (i = 1, cp = comment_buf + 1;
cp < comment_buf + comlen;
++cp)
if (*(cp - 1) == '\0')
the_hdr->comments[i++] = cp;
the_hdr->comments[i] = NULL;
} else
the_hdr->comments = NULL;
} else
the_hdr->comments = NULL;
/* Initialize state for rle_getrow */
the_hdr->priv.get.scan_y = the_hdr->ymin;
the_hdr->priv.get.vert_skip = 0;
the_hdr->priv.get.is_eof = 0;
the_hdr->priv.get.is_seek = ftell(infile) > 0;
debug_f = 0;
if (!feof(infile))
return RLE_SUCCESS; /* success! */
else {
the_hdr->priv.get.is_eof = 1;
return RLE_EOF;
}
}
void
rle_get_setup_ok(rle_hdr * const the_hdr,
const char * const prog_name,
const char * const file_name) {
/*-----------------------------------------------------------------------------
Read the initialization information from an RLE file.
Inputs:
the_hdr: Contains pointer to the input file.
prog_name: Program name to be printed in the error message.
file_name: File name to be printed in the error message.
If NULL, the string "stdin" is generated.
Outputs:
the_hdr: Initialized with information from the input file.
If reading the header fails, it prints an error message
and exits with the appropriate status code.
Algorithm:
rle_get_setup does all the work.
---------------------------------------------------------------------------- */
int code;
/* Backwards compatibility: if is_init is not properly set,
* initialize the header.
*/
if (the_hdr->is_init != RLE_INIT_MAGIC) {
FILE * const f = the_hdr->rle_file;
rle_hdr_init( the_hdr );
the_hdr->rle_file = f;
rle_names(the_hdr, prog_name, file_name, 0);
}
code = rle_get_error(rle_get_setup(the_hdr),
the_hdr->cmd, the_hdr->file_name);
if (code)
exit(code);
}
void
rle_debug( on_off )
int on_off;
{
/*-----------------------------------------------------------------------------
Turn RLE debugging on or off.
Inputs:
on_off: if 0, stop debugging, else start.
Outputs:
Sets internal debug flag.
Assumptions:
[None]
Algorithm:
[None]
---------------------------------------------------------------------------- */
debug_f = on_off;
/* Set line buffering on stderr. Character buffering is the default, and
* it is SLOOWWW for large amounts of output.
*/
setvbuf(stderr, NULL, _IOLBF, 0);
}
int
rle_getrow(rle_hdr * const the_hdr,
rle_pixel ** const scanline) {
/*-----------------------------------------------------------------------------
Get a scanline from the input file.
Inputs:
the_hdr: Header structure containing information about
the input file.
Outputs:
scanline: an array of pointers to the individual color
scanlines. Scanline is assumed to have
the_hdr->ncolors pointers to arrays of rle_pixel,
each of which is at least the_hdr->xmax+1 long.
Returns the current scanline number.
Assumptions:
rle_get_setup has already been called.
Algorithm:
If a vertical skip is being executed, and clear-to-background is
specified (the_hdr->background is true), just set the
scanlines to the background color. If clear-to-background is
not set, just increment the scanline number and return.
Otherwise, read input until a vertical skip is encountered,
decoding the instructions into scanline data.
If ymax is reached (or, somehow, passed), continue reading and
discarding input until end of image.
---------------------------------------------------------------------------- */
FILE * const infile = the_hdr->rle_file;
rle_pixel * scanc;
int scan_x; /* current X position */
int max_x; /* End of the scanline */
int channel; /* current color channel */
int ns; /* Number to skip */
int nc;
short word, long_data;
char inst[2];
scan_x = the_hdr->xmin; /* initial value */
max_x = the_hdr->xmax; /* initial value */
channel = 0; /* initial value */
/* Clear to background if specified */
if (the_hdr->background != 1) {
if (the_hdr->alpha && RLE_BIT( *the_hdr, -1))
memset((char *)scanline[-1] + the_hdr->xmin, 0,
the_hdr->xmax - the_hdr->xmin + 1);
for (nc = 0; nc < the_hdr->ncolors; ++nc) {
if (RLE_BIT( *the_hdr, nc)) {
/* Unless bg color given explicitly, use 0. */
if (the_hdr->background != 2 || the_hdr->bg_color[nc] == 0)
memset((char *)scanline[nc] + the_hdr->xmin, 0,
the_hdr->xmax - the_hdr->xmin + 1);
else
memset((char *)scanline[nc] + the_hdr->xmin,
the_hdr->bg_color[nc],
the_hdr->xmax - the_hdr->xmin + 1);
}
}
}
/* If skipping, then just return */
if (the_hdr->priv.get.vert_skip > 0) {
--the_hdr->priv.get.vert_skip;
++the_hdr->priv.get.scan_y;
if (the_hdr->priv.get.vert_skip > 0) {
if (the_hdr->priv.get.scan_y >= the_hdr->ymax) {
int const y = the_hdr->priv.get.scan_y;
while (rle_getskip(the_hdr) != 32768)
;
return y;
} else
return the_hdr->priv.get.scan_y;
}
}
/* If EOF has been encountered, return also */
if (the_hdr->priv.get.is_eof)
return ++the_hdr->priv.get.scan_y;
/* Otherwise, read and interpret instructions until a skipLines
instruction is encountered.
*/
if (RLE_BIT(*the_hdr, channel))
scanc = scanline[channel] + scan_x;
else
scanc = NULL;
for (;;) {
inst[0] = getc(infile);
inst[1] = getc(infile);
if (feof(infile)) {
the_hdr->priv.get.is_eof = 1;
break; /* <--- one of the exits */
}
switch(OPCODE(inst)) {
case RSkipLinesOp:
if (LONGP(inst)) {
VAXSHORT(the_hdr->priv.get.vert_skip, infile);
} else
the_hdr->priv.get.vert_skip = DATUM(inst);
if (debug_f)
pm_message("Skip %d Lines (to %d)",
the_hdr->priv.get.vert_skip,
the_hdr->priv.get.scan_y +
the_hdr->priv.get.vert_skip);
break; /* need to break for() here, too */
case RSetColorOp:
channel = DATUM(inst); /* select color channel */
if (channel == 255)
channel = -1;
scan_x = the_hdr->xmin;
if (RLE_BIT(*the_hdr, channel))
scanc = scanline[channel]+scan_x;
if (debug_f)
pm_message("Set color to %d (reset x to %d)",
channel, scan_x );
break;
case RSkipPixelsOp:
if (LONGP(inst)) {
VAXSHORT(long_data, infile);
scan_x += long_data;
scanc += long_data;
if (debug_f)
pm_message("Skip %d pixels (to %d)", long_data, scan_x);
} else {
scan_x += DATUM(inst);
scanc += DATUM(inst);
if (debug_f)
pm_message("Skip %d pixels (to %d)", DATUM(inst), scan_x);
}
break;
case RByteDataOp:
if (LONGP(inst)) {
VAXSHORT(nc, infile);
} else
nc = DATUM(inst);
++nc;
if (debug_f) {
if (RLE_BIT(*the_hdr, channel))
pm_message("Pixel data %d (to %d):", nc, scan_x + nc);
else
pm_message("Pixel data %d (to %d)", nc, scan_x + nc);
}
if (RLE_BIT(*the_hdr, channel)) {
/* Don't fill past end of scanline! */
if (scan_x + nc > max_x) {
ns = scan_x + nc - max_x - 1;
nc -= ns;
} else
ns = 0;
fread((char *)scanc, 1, nc, infile);
while (ns-- > 0)
getc(infile);
if (nc & 0x1)
getc(infile); /* throw away odd byte */
} else {
if (the_hdr->priv.get.is_seek)
fseek(infile, ((nc + 1) / 2) * 2, 1);
else {
int ii;
for (ii = ((nc + 1) / 2) * 2; ii > 0; --ii)
getc(infile); /* discard it */
}
}
scanc += nc;
scan_x += nc;
if (debug_f && RLE_BIT(*the_hdr, channel)) {
rle_pixel * cp;
for (cp = scanc - nc; nc > 0; --nc)
fprintf(stderr, "%02x", *cp++);
putc('\n', stderr);
}
break;
case RRunDataOp:
if (LONGP(inst)) {
VAXSHORT(nc, infile);
} else
nc = DATUM(inst);
++nc;
scan_x += nc;
VAXSHORT(word, infile);
if (debug_f)
pm_message("Run length %d (to %d), data %02x",
nc, scan_x, word);
if (RLE_BIT(*the_hdr, channel)) {
if (scan_x > max_x) {
ns = scan_x - max_x - 1;
nc -= ns;
} else
ns = 0;
if (nc >= 10) { /* break point for 785, anyway */
memset((char *)scanc, word, nc);
scanc += nc;
} else {
for (nc--; nc >= 0; --nc, ++scanc)
*scanc = word;
}
}
break;
case REOFOp:
the_hdr->priv.get.is_eof = 1;
if (debug_f)
pm_message("End of Image");
break;
default:
pm_error("rle_getrow: Unrecognized opcode: %d, reading %s",
inst[0], the_hdr->file_name);
}
if (OPCODE(inst) == RSkipLinesOp || OPCODE(inst) == REOFOp)
break; /* <--- the other loop exit */
}
/* If at end, skip the rest of a malformed image. */
if (the_hdr->priv.get.scan_y >= the_hdr->ymax) {
int const y = the_hdr->priv.get.scan_y;
while (rle_getskip(the_hdr) != 32768 )
;
return y;
}
return the_hdr->priv.get.scan_y;
}