/*************************************************************************** * * This contains routines to compress images one of two ways: * 1. Vertical Byte Run Length (VRUN) * 2. Vertical Byte Run Length With Skips (SKIP) * from amiga/programs #274, dated jul 8 1987... * * The routines here compress a single bit-plane. There's routines to * see how big the compressed result will be (xxxx_count_plane() ) * and routines to actually compress the result into a buffer in memory * (xxxx_comp_plane() ) where xxxx is either vrun or skip depending.... * * * VRUN compression format: * a VRUN-compressed plane is a concatenation of VRUN-compressed columns. * Each column is an op-count followed by a concatenation of op-count op's. * An op is in one of these two formats: * SAME_OP: 1-byte-count followed by 1-byte to repeat count times. * UNIQ_OP: 1-byte-count followed by count bytes of data to copy * The counts in either op must be 127 or less. If it's a UNIQ_OP the * hi bit of the count op is set (it's masked with 127 to find the actual * count). * * SKIP compression format: * a SKIP compressed plane is a concatenation of SKIP-compressed columns. * Like a VRUN this is an op-count followed by a concatenation of op;s. * However in this one we have 3 op formats: * SKIP_OP: Hi bit clear. Count of bytes to skip. * UNIQ_OP: Hi bit set. Remainder is count of data to copy. Data * follows immediately. * SAME_OP: A zero followed by a one-byte-count followed by one byte * of data to repeat count times. * ***************************************************************************/ #include #include #define MAXRUN 127 static WORD linebytes = 40; static WORD uniq_count; static unsigned char *uniq; static WORD op_count; /* count up how many in a column are the same between in and last_in ... ie how big of a "skip" can we go on. */ vskip(in, last_in, count) register unsigned char *in; register unsigned char *last_in; register WORD count; { register WORD skips; skips = 0; while (--count >= 0) { if (*in != *last_in) break; in += linebytes; last_in += linebytes; skips++; } return(skips); } /* vsame() - count up how many in a row (vertically) are the same as the first one ... ie how big of a "same" op we can have */ vsame(in, count) register unsigned char *in; register WORD count; { register unsigned char c; register WORD same; c = *in; in += linebytes; --count; same = 1; while (--count >= 0) { if (*in != c) break; same++; in += linebytes; } return(same); } /* skip_count_line() - figure out what size this column will compress to using vertical-byte-run-length-with-skips encoding */ skip_count_line(in, last_in, count) register unsigned char *in; register unsigned char *last_in; WORD count; { WORD local_count; WORD a_run; WORD run_length; WORD uniq_count = 0; WORD comp_count = 1; /* one for the op count */ if (vskip(in, last_in, count) == count) /* skip whole column? */ return(1); for (;;) { if (count <= 0) break; local_count = (count < MAXRUN ? count : MAXRUN); a_run = 0; if ((run_length = vskip(in, last_in, local_count)) > 1) { count -= run_length; if (count > 0) /* the last skip disappears */ comp_count += 1; a_run = 1; } else if ((run_length = vsame(in, local_count)) > 3) { count -= run_length; a_run = 1; comp_count += 3; } if (a_run) { in += run_length*linebytes; last_in += run_length*linebytes; if (uniq_count > 0) { comp_count += uniq_count+1; uniq_count = 0; } } else { in += linebytes; last_in += linebytes; uniq_count++; count -= 1; if (uniq_count == MAXRUN) { comp_count += uniq_count+1; uniq_count = 0; } } } if (count != 0) { printf("weird end count %d in skip_line_count\n"); } if (uniq_count != 0) { comp_count += uniq_count+1; } return(comp_count); } /* skip_count_plane() - figure out what size this plane will compress to using vertical-byte-run-length-with-skips encoding */ skip_count_plane(in, last_in, next_line, rows) unsigned char *in; unsigned char *last_in; WORD next_line; WORD rows; { WORD i; WORD comp_count; linebytes = next_line; comp_count = 0; i = next_line; while (--i >= 0) { comp_count += skip_count_line(in, last_in, rows); in++; last_in++; } return(comp_count); } /* flush_uniq() - write out the "uniq" run that's been accumulating while we've been looking for skips and "same" runs. */ unsigned char * flush_uniq(stuff) unsigned char *stuff; { if (uniq_count > 0) { op_count++; *stuff++ = (uniq_count | 0x80); copy_line_to_chars(uniq, stuff, linebytes, uniq_count); stuff += uniq_count; uniq_count = 0; } return(stuff); } copy_line_to_chars(in,out,linebytes,count) unsigned char *in,*out; int linebytes,count; { while (count--) { *out = *in; out++; in += linebytes; } return(0); } /* skip_comp_line() - Compress "in" into "out" using vertical-byte-run- with-skips encodeing. Return pointer to "out"'s next free space. */ unsigned char * skip_comp_line(in, last_in, out, count) register unsigned char *in; unsigned char *last_in; unsigned char *out; WORD count; { register unsigned char *stuffit; WORD local_count; WORD a_run; WORD run_length; /* if can skip over whole column, then compact a bit by just setting the "op count" for this column to zero */ if (vskip(in, last_in, count) == count) /* skip whole column? */ { *out++ = 0; return(out); } op_count = 0; /* haven't done any op's yet */ /* initialize variables which keep track of how many uniq bytes we've gone past, and where uniq run started. */ uniq_count = 0; uniq = in; stuffit = out+1; /* skip past "op-count" slot in out array */ for (;;) { if (count <= 0) break; local_count = (count < MAXRUN ? count : MAXRUN); a_run = 0; /* first assume not a skip or a same run */ /* see how much could skip from here. Two or more is worth skipping! */ if ((run_length = vskip(in, last_in, local_count)) > 1) { a_run = 1; count -= run_length; stuffit = flush_uniq(stuffit); /* flush pending "uniq" run */ if (count > 0) /* last skip vanishes */ { op_count++; *stuffit++ = run_length; } } /* four or more of the same byte in a row compresses too */ else if ((run_length = vsame(in, local_count)) > 3) { a_run = 1; count -= run_length; op_count++; stuffit = flush_uniq(stuffit); /* flush pending "uniq" run */ *stuffit++ = 0; *stuffit++ = run_length; *stuffit++ = *in; } /* if it's a run of some sort update in and last_in pointer, and reset the uniq pointer to the current position */ if (a_run) { in += run_length*linebytes; last_in += run_length*linebytes; uniq = in; /* otherwise just continue accumulating stuff in uniq for later flushing or immediate if it get's past MAXRUN */ } else { in += linebytes; last_in += linebytes; uniq_count++; count -= 1; if (uniq_count == MAXRUN) { stuffit = flush_uniq(stuffit); uniq = in; } } } /* if came to end of column within a uniq-run still have to flush it */ if (uniq_count != 0) { stuffit = flush_uniq(stuffit); } if (count != 0) { printf("weird end count %d in skip_line_count\n", count); } /* and stuff the first byte of this (compressed) column with the op_count */ *out = op_count; return(stuffit); } /* skip_comp_plane() - Compress "in" into "out" using vertical-byte-run- with-skips encodeing. Return pointer to "out"'s next free space. */ unsigned char * skip_comp_plane(in, last_in, out, next_line, rows) unsigned char *in; unsigned char *last_in; unsigned char *out; WORD next_line; WORD rows; { WORD i; unsigned char *last_out = out; linebytes = next_line; i = next_line; while (--i >= 0) { out = skip_comp_line(in, last_in, out, rows); last_out = out; in++; last_in++; } return(out); } /* vrun_count_line() - figure out what size this column will compress to using vertical-byte-run-length encoding */ vrun_count_line(in, count) register unsigned char *in; WORD count; { WORD local_count; WORD a_run; WORD run_length; WORD uniq_count = 0; WORD comp_count = 1; /* one for the op count */ for (;;) { if (count <= 0) break; local_count = (count < MAXRUN ? count : MAXRUN); a_run = 0; if ((run_length = vsame(in, local_count)) > 2) { a_run = 1; comp_count += 2; } if (a_run) { in += run_length*linebytes; count -= run_length; if (uniq_count > 0) { comp_count += uniq_count+1; uniq_count = 0; } } else { in += linebytes; uniq_count++; count -= 1; if (uniq_count == MAXRUN) { comp_count += uniq_count+1; uniq_count = 0; } } } if (count != 0) { printf("weird end count %d in vrun_line_count\n"); } if (uniq_count != 0) { comp_count += uniq_count+1; } return(comp_count); } /* vrun_count_plane() - figure out what size this plane will compress to using vertical-byte-run-length encoding */ vrun_count_plane(in, next_line, rows) unsigned char *in; WORD next_line; WORD rows; { WORD i; WORD comp_count; linebytes = next_line; comp_count = 0; i = next_line; while (--i >= 0) { comp_count += vrun_count_line(in, rows); in++; } return(comp_count); } /* vrun_comp_line() - Compress "in" into "out" using vertical-byte-run encodeing. Return pointer to "out"'s next free space. */ unsigned char * vrun_comp_line(in, out, count) register unsigned char *in; unsigned char *out; WORD count; { register unsigned char *stuffit; WORD local_count; WORD a_run; WORD run_length; uniq_count = op_count = 0; uniq = in; stuffit = out+1; for (;;) { if (count <= 0) break; local_count = (count < MAXRUN ? count : MAXRUN); a_run = 0; if ((run_length = vsame(in, local_count)) > 2) { a_run = 1; stuffit = flush_uniq(stuffit); *stuffit++ = run_length; *stuffit++ = *in; } if (a_run) { op_count++; in += run_length*linebytes; count -= run_length; uniq = in; } else { in += linebytes; uniq_count++; count -= 1; if (uniq_count == MAXRUN) { stuffit = flush_uniq(stuffit); uniq = in; } } } if (uniq_count != 0) { stuffit = flush_uniq(stuffit); } if (count != 0) { printf("weird end count %d in vrun_line_count\n", count); } *out = op_count; return(stuffit); } /* vrun_comp_plane() - Compress "in" into "out" using vertical-byte-run encodeing. Return pointer to "out"'s next free space. */ unsigned char * vrun_comp_plane(in, out, next_line, rows) unsigned char *in; unsigned char *out; WORD next_line; WORD rows; { WORD i; unsigned char *last_out = out; linebytes = next_line; i = next_line; while (--i >= 0) { out = vrun_comp_line(in, out, rows); last_out = out; in++; } return(out); }