This is rlecomp.c in view mode; [Download] [Up]
/* * 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. */ /* * rlecomp.c - Digitial image compositor (The Poor Man's Pixar) * * Author: Rod Bogart and John W. Peterson * Computer Science Dept. * University of Utah * Date: Tue Feb 25 1986 * Copyright (c) 1986 Rod Bogart and John W. Peterson * * For an explanation of the compositing algorithms, see * "Compositing Digital Images", by Porter and Duff, * SIGGRAPH 84, p.255. */ #include <stdio.h> #include <rle.h> #include <rle_raw.h> #ifdef USE_STDLIB_H #include <stdlib.h> #else #ifdef USE_STRING_H #include <string.h> #else #include <strings.h> #endif #ifdef VOID_STAR extern void *malloc(); #else extern char *malloc(); #endif extern void free(); #endif /* USE_STDLIB_H */ #define MAX(i,j) ( (i) > (j) ? (i) : (j) ) #define MIN(i,j) ( (i) < (j) ? (i) : (j) ) #define MALLOC_ERR {fprintf(stderr, "%s: ran out of heap space\n", \ cmd_name(argv));exit(-2);} #define NUM_OPS 11 #define CLEAR_OP 0 #define OVER_OP 1 /* Operations */ #define IN_OP 2 /* (these must match the comp_ops table) */ #define OUT_OP 3 #define ATOP_OP 4 #define XOR_OP 5 #define PLUS_OP 6 #define MINUS_OP 7 #define DIFF_OP 8 #define ADD_OP 9 #define SUBTRACT_OP 10 #define IN_WINDOW(y,wind) ((y >= wind.ymin) && (y <= wind.ymax)) void get_scanline(); void copy_scanline(); /* * Global raw data structures for copy_scanline. */ rle_op ** Araw, **Braw; int * Anraw, *Bnraw; rle_pixel * non_zero_pixels; void main(argc, argv) int argc; char *argv[]; { FILE *outfile = stdout; int i, j, k, temp; char *out_fname = NULL; int xlen; rle_hdr A_hdr, B_hdr; rle_hdr out_hdr; rle_pixel **Ascanline, **Bscanline; rle_pixel **rows; rle_pixel **scanout; register rle_pixel *Ascan, *Bscan, *scan; register rle_pixel Aalph, Balph; int int_result = 0; int oflag = 0, op_code; char *Afilename = NULL, *Bfilename = NULL, *op_name = NULL; int Askip, Bskip; /* Blank space counters for copy_scanline */ int rle_cnt, rle_err; char *err_fname; static CONST_DECL char *comp_ops[NUM_OPS] = { "clear", "over", "in", "out", "atop", "xor", "plus", "minus", "diff", "add", "subtract"}; /* * Parse arguments and set up input and output files. * op : select operator other than OV */ if (scanargs(argc, argv, "% o%-outfile!s Afile!s op!s Bfile!s", &oflag, &out_fname, &Afilename, &op_name, &Bfilename ) == 0) { exit(-1); } op_code = -1; for (i=0; i < NUM_OPS; i++) if (strcmp(op_name, comp_ops[i]) == 0) op_code = i; if (op_code == -1) { fprintf(stderr, "%s: Invalid compositor operation\n", op_name); exit(-2); } A_hdr = rle_dflt_hdr; B_hdr = rle_dflt_hdr; A_hdr.rle_file = rle_open_f(cmd_name( argv ), Afilename, "r"); B_hdr.rle_file = rle_open_f(cmd_name( argv ), Bfilename, "r"); if (A_hdr.rle_file == stdin && B_hdr.rle_file == stdin) fprintf(stderr, "Can't read both inputs from stdin!\n"); for ( rle_cnt = 0; ; rle_cnt++ ) { if ( (rle_err = rle_get_setup( &A_hdr )) != RLE_SUCCESS ) { err_fname = Afilename; break; } if ( (rle_err = rle_get_setup( &B_hdr )) != RLE_SUCCESS ) { err_fname = Bfilename; break; } out_hdr = A_hdr; out_hdr.xmin = MIN( A_hdr.xmin, B_hdr.xmin ); out_hdr.ymin = MIN( A_hdr.ymin, B_hdr.ymin ); out_hdr.xmax = MAX( A_hdr.xmax, B_hdr.xmax ); out_hdr.ymax = MAX( A_hdr.ymax, B_hdr.ymax ); out_hdr.alpha = 1; out_hdr.ncolors = MAX(A_hdr.ncolors, B_hdr.ncolors); if ( rle_cnt == 0 ) outfile = rle_open_f( cmd_name( argv ), out_fname, "w" ); out_hdr.rle_file = outfile; rle_addhist( argv, &A_hdr, &out_hdr ); xlen = out_hdr.xmax - out_hdr.xmin + 1; /* Enable all channels in output file */ for (i = -out_hdr.alpha; i < out_hdr.ncolors; i++) RLE_SET_BIT( out_hdr, i ); rle_put_setup( &out_hdr ); /* * Allocate row storage */ if (rle_row_alloc( &out_hdr, &Ascanline )) MALLOC_ERR; bzero( Ascanline[RLE_ALPHA], out_hdr.xmax + 1 ); if (rle_row_alloc( &out_hdr, &Bscanline )) MALLOC_ERR; bzero( Bscanline[RLE_ALPHA], out_hdr.xmax + 1 ); if (rle_row_alloc( &out_hdr, &scanout )) MALLOC_ERR; if (!(rows = (rle_pixel **) malloc(sizeof(rle_pixel *) * out_hdr.ncolors+out_hdr.alpha))) MALLOC_ERR; /* * Allocate raw storage */ if (rle_raw_alloc( &out_hdr, &Araw, &Anraw )) MALLOC_ERR; if (rle_raw_alloc( &out_hdr, &Braw, &Bnraw )) MALLOC_ERR; if (!(non_zero_pixels = (rle_pixel *)malloc( xlen * sizeof( rle_pixel )))) MALLOC_ERR; Askip = 0; /* Initialize counters for copy_scanline */ Bskip = 0; /* * Loop through all (possible) scanlines in the output file, * compositing each one. */ for ( j = out_hdr.ymin; j <= out_hdr.ymax ; j++) { /* * Special case - if this scanline is in picture A but not * B, don't do the compositing arithmaticly - just copy (or * don't copy) the picture information, depending on the * operation. */ if (IN_WINDOW(j, A_hdr) && !IN_WINDOW(j, B_hdr)) { switch (op_code) { case OVER_OP: case OUT_OP: case XOR_OP: case PLUS_OP: case MINUS_OP: case DIFF_OP: case SUBTRACT_OP: case ADD_OP: /**** Read the A channel and dump it ****/ copy_scanline( &A_hdr, &out_hdr, j, &Askip, Araw, Anraw, 0 ); break; case ATOP_OP: case IN_OP: /* Read the A channel, but dump one blank scanline */ copy_scanline( &A_hdr, &out_hdr, j, &Askip, Araw, Anraw, 1 ); break; } } else if ((!IN_WINDOW(j, A_hdr)) && IN_WINDOW(j, B_hdr)) /* As above - special case */ { switch (op_code) { case OVER_OP: case ATOP_OP: case XOR_OP: case PLUS_OP: case MINUS_OP: case DIFF_OP: case SUBTRACT_OP: case ADD_OP: /**** Read the B channel and dump it ****/ copy_scanline( &B_hdr, &out_hdr, j, &Bskip, Braw, Bnraw, 0 ); break; case OUT_OP: case IN_OP: /* Read the B channel, but dump one blank scanline */ copy_scanline( &B_hdr, &out_hdr, j, &Bskip, Braw, Bnraw, 1 ); break; } } else if (!IN_WINDOW(j, A_hdr) && !IN_WINDOW(j, B_hdr)) { rle_skiprow( &out_hdr, 1 ); } else { /**** Read the A channel ****/ get_scanline( &A_hdr, Ascanline, &Askip, Araw, Anraw ); /**** Read the B channel ****/ get_scanline( &B_hdr, Bscanline, &Bskip, Braw, Bnraw ); /* For each channel... */ for( k = RLE_ALPHA; k < out_hdr.ncolors; k++) { Ascan = &Ascanline[k][out_hdr.xmin]; Bscan = &Bscanline[k][out_hdr.xmin]; scan = &scanout[k][out_hdr.xmin]; for( i = out_hdr.xmin; i <= out_hdr.xmax; i++, Ascan++, Bscan++, scan++) { Aalph = Ascanline[RLE_ALPHA][i]; Balph = Bscanline[RLE_ALPHA][i]; switch (op_code) { /* Note OVER has been optimized for special cases */ case OVER_OP: /* cA * 1.0 + cB * (1-alphaA) */ if (Aalph == 0) int_result = *Bscan; else if (Aalph == 255) int_result = *Ascan; else int_result = ( *Ascan * 255 + *Bscan * (255 - Aalph))/255; break; case IN_OP: /* cA * alphaB + cB * 0.0 */ int_result = ( *Ascan * Balph ) /255; break; case OUT_OP: /* cA * (1-alphaB) + cB * 0.0 */ int_result = ( *Ascan * (255 - Balph) ) /255; break; case ATOP_OP: /* cA * alphaB + cB * (1-alphaA) */ int_result = ( *Ascan * Balph + *Bscan * (255 - Aalph) )/255; break; case XOR_OP: /* cA * (1-alphaB) + cB * (1-alphaA) */ int_result = (*Ascan * (255 - Balph) + *Bscan * (255 - Aalph) )/255; break; case PLUS_OP: int_result = ((temp = ((int)*Ascan + (int)*Bscan)) > 255) ? 255 : temp; break; /* minus is intended for subtracting images only, so * the alpha channel is explicitly set to 255. */ case MINUS_OP: if (k == RLE_ALPHA) int_result = 255; else int_result = ((temp = ((int)*Ascan - (int)*Bscan)) < 0) ? 0 : temp; break; case ADD_OP: int_result = ((temp = ((int)*Ascan + (int)*Bscan)) > 255) ? temp - 256: temp; break; case SUBTRACT_OP: int_result = ((temp = ((int)*Ascan - (int)*Bscan)) < 0) ? 256 + temp : temp; break; case DIFF_OP: int_result = abs((int)*Ascan - (int)*Bscan); break; } *scan = (rle_pixel) ((int_result > 255) ? 255 : ((int_result < 0) ? 0 : int_result)); } } /* Write out the composited data */ for( i = 0; i < out_hdr.ncolors+out_hdr.alpha; i++ ) rows[i] = &scanout[i-1][out_hdr.xmin]; rle_putrow( &rows[1], xlen, &out_hdr ); } } rle_puteof( &out_hdr ); /* Release storage. */ rle_row_free( &out_hdr, Ascanline ); rle_row_free( &out_hdr, Bscanline ); rle_row_free( &out_hdr, scanout ); free( rows ); rle_raw_free( &out_hdr, Araw, Anraw ); rle_raw_free( &out_hdr, Braw, Bnraw ); free( non_zero_pixels ); } /* Check for an error. EOF or EMPTY is ok if at least one image * has been read. Otherwise, print an error message. */ if ( rle_cnt == 0 || (rle_err != RLE_EOF && rle_err != RLE_EMPTY) ) rle_get_error( rle_err, cmd_name( argv ), err_fname ); exit( 0 ); } /* * Read a scanline from an RLE file. Fake up an alpha channel (from non- * background pixels) if alpha isn't present. */ void get_scanline( the_hdr, scanline, num_skip, tmp_raw, tmp_nraw ) rle_hdr * the_hdr; rle_pixel **scanline; int * num_skip; rle_op ** tmp_raw; /* Raw pointers for data left behind by */ int * tmp_nraw; /* copy_scanline. */ { int i,j,no_backgr; if (*num_skip > 0) /* Generate a blank (skipped) scanline */ { for( i = RLE_ALPHA; i < the_hdr->ncolors; i++ ) bzero( scanline[i], the_hdr->xmax ); (*num_skip)--; if (*num_skip == 0) *num_skip = -1; /* Flag raw data available */ return; } if (*num_skip < 0) /* num_skip < 0, use stashed raw data */ { rle_rawtorow( the_hdr, tmp_raw, tmp_nraw, scanline ); *num_skip = 0; } else rle_getrow(the_hdr, scanline ); /* If no alpha channel, then fake one up from non-background pixels */ if (!the_hdr->alpha) for( i = the_hdr->xmin; i <= the_hdr->xmax; i++) { no_backgr = 0; for( j = 0; j < the_hdr->ncolors; j++) { no_backgr = no_backgr || (scanline[j][i] != 0); } if (no_backgr) { scanline[RLE_ALPHA][i] = 255; } else { scanline[RLE_ALPHA][i] = 0; } } } /* * Read a scanline from an RLE file in raw mode, because we are about to dump * it to the outfile. Fake up an alpha channel (from non-background pixels) * if alpha isn't present. */ void copy_scanline( in_hdr, out_hdr, ypos, num_skip, out_raw, out_nraw, blank_output ) rle_hdr * in_hdr, * out_hdr; int ypos; int *num_skip; /* Number of scanlines to be skipped. */ rle_op ** out_raw; int * out_nraw; int blank_output; /* if non-zero, just eat input & blank output */ { register int i,j; register rle_pixel * ptr; int chan, fakeruns, xlen; xlen = in_hdr->xmax - in_hdr->xmin + 1; /* * If the skip counter == 0, then we read the next line normally. * If it's positive, then it tells us how many blank lines before * the next available data. If it's negative, it flags that * out_raw contains data to be used. */ SKIP_ROW: if (*num_skip > 0) /* We're in a blank space, output blanks */ { rle_skiprow( out_hdr, 1 ); (*num_skip)--; if (! *num_skip) *num_skip = -1; /* Flag data available. */ return; } if (! *num_skip) /* num_skip == 0, must read data... */ *num_skip = rle_getraw( in_hdr, out_raw, out_nraw ); else *num_skip = ypos; /* num_skip < 0, data was already there */ if (*num_skip == 32768) /* EOF, just quit. */ { rle_skiprow( out_hdr, 1 ); return; } *num_skip -= ypos; if ( *num_skip > 0 ) goto SKIP_ROW; /* It happens to the best of us... */ if (!blank_output) { /* * If no alpha channel, then fake one up from non-background pixels. * * This is not the most intelligent way to do this. It's * possible to look at the rle_ops directly, and do a set * union of them to produce the fake alpha channel rle_ops. * The algorithm to do this is not obvious to the casual * observer. If you want to take a crack at it, look at * lib/rle_putrow.c (the findruns routine) for hints. */ if (! in_hdr->alpha ) { /* * Create a "bytemask" of the non-zero pixels. */ bzero( non_zero_pixels, xlen ); for (chan = 0; chan < in_hdr->ncolors; chan++ ) for (i = 0; i < out_nraw[chan]; i++) if (out_raw[chan][i].opcode == RByteDataOp || out_raw[chan][i].u.run_val != 0) { for (ptr = &(non_zero_pixels[out_raw[chan][i].xloc]), j = out_raw[chan][i].length; j > 0; j--) *(ptr++) = (rle_pixel) 255; } /* * Collect the bytemask into real opcodes. Assume that this won't * be fragmented into byte data (it doesn't check). */ fakeruns = 0; i = 0; while ( i < xlen ) { j = 0; out_raw[RLE_ALPHA][fakeruns].opcode = RRunDataOp; out_raw[RLE_ALPHA][fakeruns].u.run_val = (rle_pixel) 255; while ( (non_zero_pixels[i] == (rle_pixel) 0) && (i < xlen) ) i++; out_raw[RLE_ALPHA][fakeruns].xloc = i; while( (non_zero_pixels[i] != (rle_pixel) 0) && (i < xlen) ) j++, i++; out_raw[RLE_ALPHA][fakeruns].length = j; if (j) fakeruns++; } out_nraw[RLE_ALPHA] = fakeruns; } /* dump the raw stuff to the output file */ rle_putraw( out_raw, out_nraw, out_hdr ); } else rle_skiprow( out_hdr, 1 ); rle_freeraw( out_hdr, out_raw, out_nraw ); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.