#ifndef lint static char sccsid[] = "@(#)pr_io.c 9.3 88/01/19 Copyright 1985 Sun Micro"; #endif /* * Copyright (c) 1985 by Sun Microsystems, Inc. */ /* * Extended pixrect operations -- dump pixrect to file -- load pixrect from * file * * Limitations: The size of the default color map is a compile-time parameter * that cannot be determined from a display pixrect. The file descriptor for * a device pixrect is hidden, so the FBIOGTYPE ioctl cannot be used. * * Clients (callers) of pr_dump are responsible for locking the display, as the * pr_rop() operation is not atomic with respect to display updates by other * processes, and there is no reliable way for pr_dump to do the locking. * * All of the planes of a color pixrect are written to the rasterfile, even if * some of the planes are disabled in the pixrect plane enable mask, as the * data in the pixrect is the "truth", not the current state of the enable * mask. * * This version has been modified to use the NeWS stdio package "psio". */ #include #include #include #include #ifdef REF #include #include #else #include #endif #include #define CMAP_SIZE 256 #define BITS_BYTE 8 #define FAILURE -1 #define CIMAGE(mprd) ((char *)((mprd)->md_image)) #define UCIMAGE(mprd) ((unsigned char *)((mprd)->md_image)) #define INPUT 0 #define OUTPUT 1 #define STDIN 0 #define STDOUT 1 #define STDERR 2 #define FILTER_NAME_PATTERN "%s/convert.%ld" #define FILTER_DIRECTORY "/usr/lib/rasfilters" #define PIPSIZ 4096 /* See uipc_pipe.c */ extern int errno; extern char *environ; #ifdef LITTLEENDIAN /* * Macros for processing rasterfiles on little endian machines. The * expected rasterfile format on any machine is the Sun rasterfile * format in big endian data order. These macros are used to read * rasterfiles on little endian machines to convert them to an order * that can be displayed correctly. The macros also get used to write * rasterfiles on little endian machines, so the result is in * "appropriate" big endian order. * NOTE that if filters or encoding are used, all bets are off here. * The file will probably read without error but will be a surprise when * displayed. */ #define LONGSIZE 4 /* assume 4 bytes in a long */ #define SHORTSIZE 2 /* assume 2 bytes in a short */ extern unsigned char ReverseBitsInByte[]; /* * Byte-swap an array of longs (this means reverse the bytes in each long). * Note that size of the array (in bytes) must be an integral number of longs. */ #define LONGSWAP(ip, size) { \ register int i; \ register unsigned char *tmp; \ register unsigned long *px; \ \ if (!((size) % LONGSIZE)) { \ tmp = (unsigned char *) (ip); \ for (i=(size)/LONGSIZE; i>0; i--) { \ px = (unsigned long *) tmp; \ *px = ( (*px << 24) | \ ((*px & 0x0000ff00) << 8) | \ ((*px & 0x00ff0000) >> 8) | \ (*px >> 24) ); \ tmp += 4; \ } \ } else \ return(PIX_ERR); \ } /* * Bit-swap each byte in an array of bytes. * Note that size (in bytes) must be an integral number of shorts. * * Little endian machines are byte-swapped within a short compared to big * endian machines; frame buffers based on little endian machines are bit- * reversed. Raster data in a monochrome rasterfile was written as shorts, * so we byte-swap the shorts and then bit-swap each byte to achieve the * effect of bit-reversing each short. The bytes were swapped when the data * was read into the buffer, however, so just bit-swap each byte to get the * bits reversed for the frame buffer. */ #define BITSWAP(ip, size) { \ register int i; \ register unsigned char *px; \ \ if (!((size) % SHORTSIZE)) { \ px = (unsigned char *) (ip); \ i = (size) + 1; \ while(--i) { \ *px = ReverseBitsInByte[*px]; \ ++px; \ } \ } \ } #endif /* * Initialize rasterfile header and output pixrect for pr_dump. */ struct pixrect * pr_dump_init(input_pr, rh, colormap, type, copy_flag) struct pixrect *input_pr; struct rasterfile *rh; colormap_t *colormap; int type, copy_flag; { /* * The input pixrect may be a display or a memory pixrect. It may not be * memory aligned, and the region selected may not be byte-sized * multiples, so if copy_flag is TRUE, make a copy of the desired region * into an aligned pixrect before writing the output file. If copy_flag * is FALSE, the input pixrect is assumed to be aligned AND can be * modified if compress_flag is TRUE; */ struct pixrect *pr = input_pr; if (copy_flag) { pr = mem_create(input_pr->pr_width, input_pr->pr_height, input_pr->pr_depth); if (pr == NULL) goto ErrorReturn; if (pr_rop(pr, 0, 0, input_pr->pr_width, input_pr->pr_height, PIX_SRC, input_pr, 0, 0) == PIX_ERR) goto ErrorReturn; } /* * Initialize rasterfile header. */ rh->ras_magic = RAS_MAGIC; rh->ras_width = pr->pr_width; rh->ras_height = pr->pr_height; rh->ras_depth = pr->pr_depth; if ((rh->ras_length = validate_pr(pr)) == FAILURE) goto ErrorReturn; if ((rh->ras_type = type) == RT_OLD) rh->ras_length = 0; /* * Above assignment is required for generation of files acceptable to 1.X * family code that expected the old ras_encoding field to be 0. */ if (colormap == NULL || colormap->type == RMT_NONE) { rh->ras_maptype = RMT_NONE; rh->ras_maplength = 0; } else if (colormap->type == RMT_EQUAL_RGB) { rh->ras_maptype = colormap->type; rh->ras_maplength = colormap->length * 3; } else goto ErrorReturn; return (pr); ErrorReturn: if ((pr != NULL) && (pr != input_pr)) (void) pr_destroy(pr); return (NULL); } /* * Dump a pixrect to the specified stream. */ int pr_dump(input_pr, output, colormap, type, copy_flag) struct pixrect *input_pr; PSFILE *output; colormap_t *colormap; int type, copy_flag; { /* * The input pixrect may be a secondary pixrect, created by pr_region, * allowing the caller to dump rectangular sub-regions of a display or * memory pixrect. * * WARNING: if type == RT_BYTE_ENCODED, copy_flag == FALSE and * run_length_encode (called via pr_dump_encoded) fails, then the input_pr * will be trashed. */ struct pixrect *copy_pr; struct rasterfile rh; int output_byte_count, result = 0; unsigned char red[CMAP_SIZE], green[CMAP_SIZE], blue[CMAP_SIZE]; colormap_t local_colormap; /* * If the colormap pointer is NULL, then get the color map from the input * pixrect (it must be a display pixrect on a color display device.) */ if (((input_pr->pr_depth) != 1) && (colormap == NULL)) { if (pr_getcolormap(input_pr, 0, CMAP_SIZE, red, green, blue) == PIX_ERR) goto ErrorReturn; colormap = &local_colormap; colormap->type = RMT_EQUAL_RGB; colormap->length = CMAP_SIZE; colormap->map[0] = red; colormap->map[1] = green; colormap->map[2] = blue; } copy_pr = pr_dump_init(input_pr, &rh, colormap, type, copy_flag); if (copy_pr == NULL) return (PIX_ERR); /* * If type is not RT_OLD or RT_STANDARD, either run-length-encode or * filter the data before writing it to the output file. */ switch (type) { case RT_BYTE_ENCODED: if (pr_dump_encoded(copy_pr, output, &rh, colormap) != 0) goto ErrorReturn; break; case RT_OLD: /* Fall through */ case RT_STANDARD: if (pr_dump_header(output, &rh, colormap) == PIX_ERR) goto ErrorReturn; if (pr_dump_image(copy_pr, output, &rh) == PIX_ERR) goto ErrorReturn; break; default: rh.ras_type = RT_STANDARD; if (pr_dump_nonstd_type(copy_pr, output, &rh, colormap, type, rh.ras_length) == PIX_ERR) goto ErrorReturn; break; } NormalReturn: if (copy_pr != input_pr) (void) pr_destroy(copy_pr); return (result); ErrorReturn: result = PIX_ERR; goto NormalReturn; } /* * Dump the header to the specified stream. */ int pr_dump_header(output, rh, colormap) PSFILE *output; register struct rasterfile *rh; register colormap_t *colormap; { #ifdef LITTLEENDIAN /* * See notes near macro definitions for details. * Because the rasterfile struct is still used here, convert * it back to the way we found it after writing it out. */ LONGSWAP(rh, sizeof(*rh)); #endif if (psio_write((char *) rh, sizeof(*rh), 1, output) != 1) { #ifdef LITTLEENDIAN LONGSWAP(rh, sizeof(*rh)); #endif return (PIX_ERR); } #ifdef LITTLEENDIAN else { LONGSWAP(rh, sizeof(*rh)); } #endif if (colormap != NULL) { register int length = colormap->length; switch (colormap->type) { case RMT_NONE: if ((rh->ras_maptype != RMT_NONE) || (rh->ras_maplength != 0)) return (PIX_ERR); break; case RMT_EQUAL_RGB: if ((rh->ras_maptype != RMT_EQUAL_RGB) || (rh->ras_maplength != 3 * length)) return (PIX_ERR); if ((length != 0) && ( (psio_write((char *) colormap->map[0], 1, length, output) != length) || (psio_write((char *) colormap->map[1], 1, length, output) != length) || (psio_write((char *) colormap->map[2], 1, length, output) != length))) return (PIX_ERR); break; default: return (PIX_ERR); } } return (0); } /* * Dump the image data to the specified stream. */ int pr_dump_image(pr, output, rh) struct pixrect *pr; PSFILE *output; register struct rasterfile *rh; { register int length; int retcode = 0; length = (rh->ras_type == RT_OLD) ? pix_image_length(rh->ras_width, rh->ras_height, rh->ras_depth) /* 1.X compatibility */ : rh->ras_length; #ifdef LITTLEENDIAN /* * See notes near macro definitions for deteails. * Because the data may be used again here, must convert it * back to the way we found it after writing it out. */ if (rh->ras_depth == 1) { BITSWAP((mpr_d(pr))->md_image, length); } #endif if (psio_write(CIMAGE(mpr_d(pr)), 1, length, output) != length) retcode = PIX_ERR; #ifdef LITTLEENDIAN if (rh->ras_depth == 1) { BITSWAP((mpr_d(pr))->md_image, length); } #endif return(retcode); } /* * Encode specified pixrect then write it onto specified output. Return values * are: 0 success PIX_ERR write failed PIX_ERR-1 * run_length_encode failed, input pr has been destroyed! */ int pr_dump_encoded(pr, output_file, rh, colormap) register struct pixrect *pr; PSFILE *output_file; register struct rasterfile *rh; register colormap_t *colormap; { register struct mpr_data *mprd = mpr_d(pr); rh->ras_length = run_length_encode(UCIMAGE(mprd), rh->ras_length); if (rh->ras_length == FAILURE) return (PIX_ERR - 1); if (pr_dump_header(output_file, rh, colormap) == PIX_ERR) return (PIX_ERR); if (psio_write(CIMAGE(mprd), 1, rh->ras_length, output_file) != rh->ras_length) return (PIX_ERR); return (0); } #ifndef REF /* * Write specified pixrect through filter then onto specified output. */ int pr_dump_nonstd_type(pr, output_file, rh, colormap, type, for_filter_size) register struct pixrect *pr; PSFILE *output_file; register struct rasterfile *rh; register colormap_t *colormap; int type, for_filter_size; { char *for_filter = CIMAGE(mpr_d(pr)); int filter_input, filter_output, output_fd = psio_fileno(output_file), max_fds = getdtablesize(), result = 0; unsigned char buffer[PIPSIZ]; FILE *to_filter_file; struct timeval tv; /* * Copy rasterfile header, etc to filter. Note use of stdio on * filter_input is limited to header output. */ if (start_filter(type, &filter_input, &filter_output) == FAILURE) return (PIX_ERR); if ((to_filter_file = fdopen(filter_input, "w")) == NULL) return (PIX_ERR); if (pr_dump_header(to_filter_file, rh, colormap) == PIX_ERR) goto ErrorReturn; if (fflush(to_filter_file) == EOF) goto ErrorReturn; /* * Copy pixrect into the filter. As filter makes the filtered rasterfile * available, copy it to output_file. Stdio is not used to read from the * filter because it is necessary to make sure that the read does not * block. In additon, for both reading and writing of the filter, the * stdio automatic buffering is only a hinderance. */ tv.tv_sec = 1; tv.tv_usec = 0; for (;;) { int nfds, readfds = (1 << filter_output), writefds = 0, exceptfds = 0; struct timeval *tv_to_use; if timerisset (&tv) { tv_to_use = &tv; } else { tv_to_use = NULL; if (for_filter_size > 0) writefds |= (1 << filter_input); } nfds = select(max_fds, &readfds, &writefds, &exceptfds, tv_to_use); switch (nfds) { case -1: goto ErrorReturn; case 0: /* Fall through */ default: timerclear(&tv); break; } if (readfds & (1 << filter_output)) { int bytes_read; bytes_read = read(filter_output, buffer, sizeof(buffer)); switch (bytes_read) { case 0: if (fflush(output_file) == EOF) goto ErrorReturn; else goto NormalReturn; case -1: goto ErrorReturn; default: if (psio_write(buffer, 1, bytes_read, output_file) != bytes_read) goto ErrorReturn; break; } } else if (writefds & (1 << filter_input)) { int to_write, written; to_write = (for_filter_size > PIPSIZ) ? PIPSIZ : for_filter_size; written = write(filter_input, for_filter, to_write); if (written == -1) { if (errno == EMSGSIZE) { /* Give filter a chance to run */ tv.tv_sec = 0; tv.tv_usec = 50000; } else goto ErrorReturn; } else { for_filter += written; for_filter_size -= written; } } } NormalReturn: (void) fclose(to_filter_file); /* This close's underlying fd */ (void) close(filter_output); /* * No errors encountered, so return the completed pixrect. */ return (result); ErrorReturn: result = PIX_ERR; goto NormalReturn; } #endif /* * Load the color map from the specified stream. */ int pr_load_colormap(input, rh, colormap) PSFILE *input; register struct rasterfile *rh; register colormap_t *colormap; { if (colormap == NULL) { /* * The client is assuming that it already has the correct color map. * As a service, we discard the color map by reading over it. WARNING: * we don't use fseek in case input is a pipe. */ register int bytes_to_discard = rh->ras_maplength; while (bytes_to_discard--) { (void) psio_getc(input); } return (0); } if (rh->ras_maptype != colormap->type) return (PIX_ERR); switch (colormap->type) { case RMT_NONE: return (0); case RMT_EQUAL_RGB: if (rh->ras_maplength != colormap->length * 3) return (PIX_ERR); if ((colormap->length != 0) && ( (1 != psio_read((char *) colormap->map[0], colormap->length, 1, input)) || (1 != psio_read((char *) colormap->map[1], colormap->length, 1, input)) || (1 != psio_read((char *) colormap->map[2], colormap->length, 1, input)))) return (PIX_ERR); return (0); default: return (PIX_ERR); } } /* * Load the header from the specified stream. */ int pr_load_header(input, rh) PSFILE *input; register struct rasterfile *rh; { /* * Read in the rasterfile header and do some validity checking. Note: a * colormap may legitimately exist for a depth=1 pixrect, although it is * usually an error. However, old code has already generated such files, * so don't complain about them. */ /* * The rasterfile struct is built of ints. Portability assumes * that ints are 4 bytes everywhere. Rasterfiles should be altered * to use XDR; */ if (psio_read((char *) rh, sizeof(*rh), 1, input) != 1) return (PIX_ERR); #ifdef LITTLEENDIAN /* * See notes near macro definitions for details. */ LONGSWAP(rh, sizeof(*rh)); #endif if (rh->ras_magic != RAS_MAGIC) return (PIX_ERR); return (0); } /* * Load pixrect image from the specified header and stream. */ struct pixrect * pr_load_std_image(input, rh) PSFILE *input; register struct rasterfile *rh; { struct pixrect *mem_pr; int n, input_byte_count, pixrect_buffer_size; char *input_buffer, *output_buffer; pixrect_buffer_size = pix_image_length( rh->ras_width, rh->ras_height, rh->ras_depth); switch (rh->ras_type) { case RT_OLD: input_byte_count = pixrect_buffer_size; break; case RT_STANDARD: /* Fall through */ case RT_BYTE_ENCODED: input_byte_count = rh->ras_length; break; default: return (NULL); } if ((mem_pr = mem_create(rh->ras_width, rh->ras_height, rh->ras_depth)) == NULL) return (NULL); /* avoid psio_read of 0 bytes first */ if (input_byte_count == 0) return (mem_pr); input_buffer = output_buffer = CIMAGE(mpr_d(mem_pr)); if (rh->ras_type == RT_BYTE_ENCODED) input_buffer += pixrect_buffer_size - input_byte_count; if (psio_read(input_buffer, input_byte_count, 1, input) != 1) { (void) pr_destroy(mem_pr); return (NULL); } if (rh->ras_type == RT_BYTE_ENCODED) { if (run_length_decode((unsigned char *) input_buffer, (unsigned char *) output_buffer, input_byte_count, pixrect_buffer_size) != pixrect_buffer_size) { (void) pr_destroy(mem_pr); return (NULL); } } #ifdef LITTLEENDIAN /* * See notes near macro definitions for details. */ if (rh->ras_depth == 1) BITSWAP(input_buffer, input_byte_count); #endif /* * No errors encountered, so return the completed pixrect. */ return (mem_pr); } #ifndef REF /* * Load pixrect image from the specified stream, header and colormap. If the * image must be filtered, the header and colormap will be modified. */ struct pixrect * pr_load_image(input, rh, colormap) PSFILE *input; register struct rasterfile *rh; register colormap_t *colormap; { struct pixrect *mem_pr = NULL; int input_byte_count; char *input_buffer; int filter_input, filter_output, bytes_read, for_filter_size = 0, input_available = 1, got_new_header = 0, max_fds = getdtablesize(); PSFILE *from_filter_file; FILE *to_filter_file; unsigned char buffer[PIPSIZ], *for_filter; struct timeval tv; switch (rh->ras_type) { case RT_OLD: /* Fall through */ case RT_STANDARD: /* Fall through */ case RT_BYTE_ENCODED: return (pr_load_std_image(input, rh)); default: break; } /* * Since the rasterfile is of non-standard type, attempt to pipe it to one * of the well-known filters, and read the result instead. */ if (start_filter(rh->ras_type, &filter_input, &filter_output) == FAILURE) goto ErrorReturn; if ((to_filter_file = fdopen(filter_input, "w")) == NULL) goto ErrorReturn; if ((from_filter_file = psio_fdopen(filter_output)) == NULL) goto ErrorReturn; if (pr_dump_header(to_filter_file, rh, colormap) == PIX_ERR) goto ErrorReturn; if (fflush(to_filter_file) == EOF) goto ErrorReturn; /* * Copy our input into the filter. As soon as the filter makes the new * rasterfile header available, read it and allocate mem_pr. Read filtered * rasterfile into mem_pr, making sure that priority is given to moving * data from our input through the filter. Avoid stdio in order to control * buffering to avoid deadly embrace. */ tv.tv_sec = 1; tv.tv_usec = 0; for (;;) { int nfds, readfds, writefds, exceptfds = 0; struct timeval *tv_to_use; if (input_available) { if (for_filter_size == 0) { for_filter_size = psio_read(buffer, 1, sizeof(buffer), input); for_filter = buffer; if (for_filter_size == 0) { input_available = 0; } } else { int written = write(filter_input, for_filter, for_filter_size); if (written == -1) { if (errno == EMSGSIZE) { /* Give filter a chance to run */ tv.tv_sec = 0; tv.tv_usec = 50000; } else goto ErrorReturn; } else { for_filter += written; for_filter_size -= written; } } writefds = (1 << filter_input); } else writefds = 0; readfds = (1 << filter_output); if timerisset (&tv) { tv_to_use = &tv; writefds = 0; } else { tv_to_use = NULL; } nfds = select(max_fds, &readfds, &writefds, &exceptfds, tv_to_use); switch (nfds) { case -1: goto ErrorReturn; case 0: /* Fall through */ default: timerclear(&tv); break; } if (readfds & (1 << filter_output)) { if (got_new_header) { bytes_read = read(filter_output, input_buffer, input_byte_count); switch (bytes_read) { case 0: case -1: goto ErrorReturn; default: break; } input_buffer += bytes_read; if ((input_byte_count -= bytes_read) == 0) break; } else { (void) setbuf(from_filter_file, NULL); if (pr_load_header(from_filter_file, rh) == PIX_ERR) goto ErrorReturn; if (pr_load_colormap(from_filter_file, rh, colormap) == PIX_ERR) goto ErrorReturn; switch (rh->ras_type) { case RT_OLD: /* Compatibility for 2.X */ rh->ras_length = pix_image_length(rh->ras_width, rh->ras_height, rh->ras_depth); /* Fall through */ case RT_STANDARD: input_byte_count = rh->ras_length; break; default: goto ErrorReturn; } if ((mem_pr = mem_create( rh->ras_width, rh->ras_height, rh->ras_depth)) == NULL) goto ErrorReturn; input_buffer = CIMAGE(mpr_d(mem_pr)); got_new_header = !0; } } } NormalReturn: (void) fclose(to_filter_file); (void) fclose(from_filter_file); /* * No errors encountered, so return the completed pixrect. */ return (mem_pr); ErrorReturn: if (mem_pr != NULL) { (void) pr_destroy(mem_pr); mem_pr = NULL; } goto NormalReturn; } #endif /* * Load a pixrect from the specified stream. */ struct pixrect * pr_load(input, colormap) PSFILE *input; colormap_t *colormap; { struct rasterfile rh; register c; while ((c = psio_getc(input)) != EOF && c != ' ' && c != '\n' && c != '\t'); psio_ungetc(c, input); if (pr_load_header(input, &rh) == PIX_ERR) return (NULL); if (pr_load_colormap(input, &rh, colormap) == PIX_ERR) return (NULL); return ((struct pixrect *)pr_load_image(input, &rh, colormap)); } /* * The "run-length encoding" is defined as follows: * * First, the rasterfile header should have a field defining the length of the * raster data which follows, but it does not. This is computable from the * width, height, and depth for unencoded data, but not for encoded data. * Therefore the first four bytes of the encoded data are defined to be an * integer specifying the actual length of the encoded bitmap. The first byte * is the most significant part of the integer; the fourth byte is the least * significant part. The output byte count returned by this procedure * INCLUDES the four bytes for this length variable. * * Second, I have tested two encoding algorithms on actual screendump data to see * which yields the best compression. The first encoding is of the form * * ....... * * where the counts are in the range 0..255 and the actual number of instances of * is +1 (i.e. actual is 1..256). The second encoding is of the * form * * ...<0>...... * * where the same definition of applies. One- or two-character sequences * are left unencoded; three-or-more character sequences are encoded as * . is one special character code, not necessarily * the ASCII character ESC. Each single in the input data stream is * encoded as <0>, because the in this scheme can never be 0 * (actual count can never be 1). * * The second algorithm consistently yields better compression than the first. * Also, it will fail (make the "compressed" data bigger than the original * data) only if the input stream contains an excessive number of one- and * two-character sequences of the character. Judicious choice of the * character will avoid this problem (don't use "0" or "0xff"). A * one-pass scan of the input data could be used to select a character that * would be sure to work, though that would require specifying the * character in the encoded data stream (probably as byte 5). * * The first algorithm will fail any time there are too many one-character * sequences of any character. */ #define BUF_SIZ 1024 #define ESCAPE ((unsigned char) '\200') #define ALGORITHM2 1 /* * The routines below implement algorithm number two (described above). These * routines are the ones used by "pr_dump" and "pr_load" today. */ #ifdef ALGORITHM2 int run_length_encode(input_data, input_byte_count) register unsigned char *input_data; register int input_byte_count; { /* * WARNING! THIS ROUTINE HAS SIDE-EFFECTS! * * This routine attempts to run-length-encode the byte array passed as input * data into the same space. If the compression fails, the input data * will have been destroyed. */ register int count; register unsigned char c1, c2, *compressed_data; int i, outcount, output_byte_count = 0, temp_byte_count = 0; unsigned char temp_buffer[BUF_SIZ]; if (input_byte_count < 3) return (FAILURE); c1 = *input_data; count = 1; compressed_data = input_data - 1; while (1) { while (--input_byte_count > 0) { c2 = *++input_data; if ((c1 != c2) || (count == 256)) break; count++; } if (c1 == ESCAPE) outcount = ((count == 1) ? 2 : 3); else outcount = ((count < 3) ? count : 3); if ((compressed_data + outcount + temp_byte_count) < input_data) { for (i = 0; i < temp_byte_count; i++) *++compressed_data = temp_buffer[i]; output_byte_count += temp_byte_count; temp_byte_count = 0; if ((c1 == ESCAPE) && (count == 1)) { *++compressed_data = ESCAPE; *++compressed_data = '\000'; output_byte_count += 2; } else if ((c1 != ESCAPE) && (count <= 2)) { *++compressed_data = c1; if (count == 2) *++compressed_data = c1; output_byte_count += count; } else { *++compressed_data = ESCAPE; *++compressed_data = (unsigned char) (count - 1); *++compressed_data = c1; output_byte_count += 3; } } else { if ((temp_byte_count + outcount) > BUF_SIZ) return (FAILURE); if ((c1 == ESCAPE) && (count == 1)) { temp_buffer[++temp_byte_count] = ESCAPE; temp_buffer[++temp_byte_count] = '\000'; } else if ((c1 != ESCAPE) && (count <= 2)) { temp_buffer[++temp_byte_count] = c1; if (count == 2) temp_buffer[++temp_byte_count] = c1; } else { temp_buffer[++temp_byte_count] = ESCAPE; temp_buffer[++temp_byte_count] = (unsigned char) (count - 1); temp_buffer[++temp_byte_count] = c1; } } if (input_byte_count == 0) { if (temp_byte_count != 0) return (FAILURE); return (output_byte_count); } c1 = c2; count = 1; } } int run_length_decode(input_data, output_data, compressed_byte_count, maximum_buffer_size) unsigned char *input_data, *output_data; int compressed_byte_count, maximum_buffer_size; { /* * WARNING! THIS ROUTINE MAY HAVE SIDE-EFFECTS! * * The input data buffer and the output data buffer are allowed to be totally * or partially contiguous. If there is overlap, this routine attempts to * run-length-decode the byte array passed as input data into the same * space. If the expansion fails, the input data will have been * destroyed. */ int expanded_byte_count = 0; int i, count, temp_buffer_count = 0; unsigned char c; unsigned char temp_buffer[BUF_SIZ]; input_data--; output_data--; while (--compressed_byte_count >= 0) { c = *++input_data; count = 1; if (c == ESCAPE) { if (--compressed_byte_count < 0) return (FAILURE); count = ((int) *++input_data); if (count != 0) if (--compressed_byte_count < 0) return (FAILURE); else { c = *++input_data; } count++; } if ((expanded_byte_count += count) > maximum_buffer_size) return (FAILURE); /* * If data is in the temporary buffer and there is room to copy it * into the output buffer, then copy temp -> output. * * If no data is in the temporary buffer then expand this byte into the * output buffer. * * If data is in the temporary buffer and there is no room to copy it * into the output buffer, or If there was not enough room to complete * the expansion of this byte into the output buffer, then expand [the * rest of] this byte into the output buffer. */ if ((temp_buffer_count > 0) && ((output_data > input_data) || ((output_data + temp_buffer_count) > input_data))) { for (i = 1; i < temp_buffer_count; i++) *++output_data = temp_buffer[i]; temp_buffer_count = 0; } if (temp_buffer_count == 0) if (output_data <= input_data) while ((output_data <= input_data) && (--count >= 0)) *++output_data = c; else while (--count >= 0) *++output_data = c; if (count >= 0) { if ((temp_buffer_count + count) >= BUF_SIZ) return (FAILURE); while (--count >= 0) temp_buffer[++temp_buffer_count] = c; } } if (temp_buffer_count != 0) return (FAILURE); else return (expanded_byte_count); } #endif /* ALGORITHM2 */ /* * The routines below implement algorithm number one (described above). These * routines are not used by "pr_dump" and "pr_load" today. */ #ifdef ALGORITHM1 int run_length_encode(input_data, input_byte_count) unsigned char *input_data; int input_byte_count; { /* * WARNING! THIS ROUTINE HAS SIDE-EFFECTS! * * This routine attempts to run-length-encode the byte array passed as input * data into the same space. If the expansion fails, the input data will * have been destroyed. */ int i, count, output_byte_count = 0, temp_byte_count = 0; unsigned char c1, c2, *compressed_data; unsigned char temp_buffer[BUF_SIZ]; if (input_byte_count < 2) return (FAILURE); c1 = *input_data; count = 1; compressed_data = input_data - 1; while (1) { while (--input_byte_count > 0) { c2 = *++input_data; if ((c1 != c2) || (count == 256)) break; count++; } if ((compressed_data + 2 + temp_byte_count) < input_data) { for (i = 0; i < temp_byte_count; i++) *++compressed_data = temp_buffer[i]; *++compressed_data = (unsigned char) (count - 1); *++compressed_data = c1; output_byte_count += temp_byte_count + 2; temp_byte_count = 0; } else { if (temp_byte_count == BUF_SIZ) return (FAILURE); temp_buffer[++temp_byte_count] = (unsigned char) (count - 1); temp_buffer[++temp_byte_count] = c1; } if (input_byte_count == 0) { if (temp_byte_count != 0) return (FAILURE); return (output_byte_count); } c1 = c2; count = 1; } } int run_length_decode(input_data, output_data, compressed_byte_count, maximum_buffer_size) unsigned char *input_data, *output_data; int compressed_byte_count, maximum_buffer_size; { /* * WARNING! THIS ROUTINE MAY HAVE SIDE-EFFECTS! * * The input data buffer and the output data buffer are allowed to be totally * or partially contiguous. If there is overlap, this routine attempts to * run-length-decode the byte array passed as input data into the same * space. If the expansion fails, the input data will have been * destroyed. */ int expanded_byte_count = 0; int i, count, temp_buffer_count = 0; unsigned char c; unsigned char temp_buffer[BUF_SIZ]; input_data--; output_data--; if ((compressed_byte_count % 2) != 0) return (FAILURE); for (; compressed_byte_count > 0; compressed_byte_count -= 2) { count = ((int) *++input_data) + 1; c = *++input_data; if ((expanded_byte_count += count) > maximum_buffer_size) return (FAILURE); /* * If data is in the temporary buffer and there is room to copy it * into the output buffer, then copy temp -> output. * * If no data is in the temporary buffer then expand this byte into the * output buffer. * * If data is in the temporary buffer and there is no room to copy it * into the output buffer, or If there was not enough room to complete * the expansion of this byte into the output buffer, then expand [the * rest of] this byte into the output buffer. */ if ((temp_buffer_count > 0) && ((output_data > input_data) || ((output_data + temp_buffer_count) > input_data))) { for (i = 1; i < temp_buffer_count; i++) *++output_data = temp_buffer[i]; temp_buffer_count = 0; } if (temp_buffer_count == 0) if (output_data <= input_data) while ((output_data <= input_data) && (--count >= 0)) *++output_data = c; else while (--count >= 0) *++output_data = c; if (count >= 0) { if ((temp_buffer_count + count) >= BUF_SIZ) return (FAILURE); while (--count >= 0) temp_buffer[++temp_buffer_count] = c; } } if (temp_buffer_count != 0) return (FAILURE); else return (expanded_byte_count); } #endif /* ALGORITHM1 */ int make_deep_pr(mem_pr, colormap_index, target_depth) struct pixrect **mem_pr; int colormap_index, target_depth; { /* * THIS ROUTINE HAS SIDE-EFFECTS. It destroys the pixrect pointed to by * its argument and replaces it with a pixrect of the same "pattern" but * with each pixel of the desired depth, with value "color_map_index". * * The input pixrect "mem_pr" must be a memory pixrect. The procedure for * making it deep uses knowledge of the public layout of memory pixrects * to optimize the code for fast execution time. The target depth must be * 8 bits (one unsigned char) or the optimized code will not work. */ struct pixrect *deep_mem_pr; int w, h, x, y; struct pr_pos origin; short *shallow_image, *deep_image, *shallow_row; register unsigned char *deep_row; register short c, t; w = ((*mem_pr)->pr_width); h = ((*mem_pr)->pr_height); if ((((*mem_pr)->pr_depth) != 1) || (target_depth != BITS_BYTE)) return (PIX_ERR); deep_mem_pr = (struct pixrect *) mem_create(w, h, target_depth); /* * This double loop will operate correctly regardless of pixrect depth and * dimensions, but it is too slow for normal use. * * for(x=0; xmd_offset; if ((origin.x != 0) || (origin.y != 0)) return (PIX_ERR); origin = mpr_d(*mem_pr)->md_offset; if ((origin.x != 0) || (origin.y != 0)) return (PIX_ERR); deep_image = mpr_d(deep_mem_pr)->md_image; deep_image--; shallow_image = mpr_d(*mem_pr)->md_image; shallow_image--; for (y = h; y > 0; y--) { deep_row = (unsigned char *) deep_image; deep_row++; deep_image += mpr_mdlinebytes(deep_mem_pr) / 2; shallow_row = shallow_image; shallow_image += mpr_mdlinebytes(*mem_pr) / 2; for (x = (w / MPR_LINEBITPAD); x > 0; x--) { c = *++shallow_row; for (t = MPR_LINEBITPAD; t > 0; t--) { *++deep_row = (((c & 0x80000000) != 0) ? colormap_index : 0); c <<= 1; } } for ((t = (w % MPR_LINEBITPAD)); t > 0; t--) { *++deep_row = (((c & 0x80000000) != 0) ? colormap_index : 0); c <<= 1; } } if (pr_destroy(*mem_pr) == PIX_ERR) return (PIX_ERR); *mem_pr = deep_mem_pr; return (0); } #ifdef SUN static int #endif start_filter(type, filter_input, filter_output) int type; int *filter_input, *filter_output; { int to_filter[2], from_filter[2], pid, max_fds = getdtablesize(); char filter_name[sizeof(FILTER_NAME_PATTERN) + sizeof(FILTER_DIRECTORY) + 10]; sprintf(filter_name, FILTER_NAME_PATTERN, (type == RT_EXPERIMENTAL) ? "." : FILTER_DIRECTORY, type); if ((pipe(to_filter) != 0) || (pipe(from_filter) != 0)) return (FAILURE); if ((pid = fork()) < 0) return (FAILURE); if (pid == 0) { /* child */ int fd; char *my_argv[2]; /* * Fiddle with the file descriptors, since the exec'd filter expects * to read the raster data from stdin, and to write the decoded data * to stdout. */ if ((dup2(to_filter[INPUT], STDIN) == -1) || (dup2(from_filter[OUTPUT], STDOUT) == -1)) return (FAILURE); for (fd = STDERR + 1; fd < max_fds; fd++) close(fd); my_argv[0] = filter_name; my_argv[1] = 0; (void) execvp(my_argv[0], my_argv, environ); return (FAILURE); } /* parent */ if ((close(to_filter[INPUT]) == -1) || (close(from_filter[OUTPUT]) == -1)) return (FAILURE); /* * The pipes to the filter must be non-blocking so that caller can avoid * deadly embrace while pushing the data through the filter. */ if (fcntl(to_filter[OUTPUT], F_SETFL, O_NDELAY) == -1) return (FAILURE); if (fcntl(from_filter[INPUT], F_SETFL, O_NDELAY) == -1) return (FAILURE); *filter_input = to_filter[OUTPUT]; *filter_output = from_filter[INPUT]; return (0); } #ifdef SUN static int #endif pix_image_length(width, height, depth) int width, height, depth; { /* * Computes the appropriate ras_length for unencoded rasterfiles. * Therefore, this is also correct for ras_type == RT_OLD rasterfiles. */ return (mpr_linebytes(width, depth) * height); } static int validate_pr(pr) register struct pixrect *pr; { /* * pr_dump* routines assume that the byte array which implements the * memory pixrect is contiguous in memory except for a possible one byte * of padding at the end of each horizontal strip of pixels, so verify * this assumption. */ register int bit_width = ((pr->pr_width * pr->pr_depth) + MPR_LINEBITPAD - 1) & ~(MPR_LINEBITPAD - 1); if (mpr_mdlinebytes(pr) != (bit_width / BITS_BYTE)) return (FAILURE); return ((bit_width * pr->pr_height) / BITS_BYTE); }