/*******************************************************************
* file: FAXSCALE.C
* purpose: Minimalist scaler. Scales images over a range of 3% to 3200% with
* a worst case precision of 1/32. This code was designed to minimize data,
* stack and code requirements while being reasonably fast and accurate. Since
* the intended use is in fax machine, the scaled lines are clipped or white
* filled to FAX_WIDTH bytes. Refer to the function headers for more detailed
* information. Image lines follow the usual monochromatic format of 1 being
* black, 0 being white and bit 7 of each byte being the leftmost pixel. This
* code is an algorithm test bench. The production version is in assembly.
* environment: ANSI C tested with Borland 4.0, Zortech 3.1 and
* MSC 5.1
* contains:
* init_fax_scale() - initializes the scaler
* scale_fax_line() - scales a single image line
* history:
* 06-09-92 - initial code
* 04-04-94 - polished, reformatted, retested, condensed
******************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
/* data types and defines */
typedef unsigned long ROTATOR; /* rotator type sets scaling
* range and precision*/
#define PRECISION (4 * 8) /* ANSI doesn't allow sizeof(ROTATOR)
* in preprocessor */
#define SCALE_MAX (PRECISION * 100) /* upper limit of scaler in % */
#define SCALE_MIN (100 / PRECISION) /* lower limit of scaler in % */
#define FAX_WIDTH 216 /* hardwired byte width of a fax line */
/* rotate instruction, just a kludge until assembly */
#define _ror(val) (val) = ((val) >> 1 | (val) << (PRECISION-1))
/* ROMmable const data */
/* sequential increase in bits per byte: do not change
* without looking closely at init_rotator( ) */
static const unsigned char pattern[] =
{0x00,0x10,0x44,0x94,0xaa,0xda,0xee,0xfe,0xff,0xff};
/* RAM data
*/ static ROTATOR x_rotator; /* rotating horizontal pattern */
static ROTATOR y_rotator; /* rotating vertical pattern */
static unsigned int source_width; /* number of bytes in a source line */
static unsigned char x_base; /* n/8 basis for x_rotator */
static unsigned char y_base; /* n/8 basis for y_rotator */
/* local functions */
static int init_rotator(int scale,ROTATOR *rotator,unsigned char *base);
/*********************************************************************
*function: int init_fax_scale(int x_scale,int y_scale,unsigned int source_byte_width)
* Validate the scaling ranges and initialize the scaler. You
* must call this function first before you can use the scaler.
* parameters: horizontal and vertical scaling in percent followed
* by the byte width of a source line.
* returns: 1 if Ok or 0 if either scale is out of range ********************************************************************/
int init_fax_scale(int x_scale,int y_scale,unsigned int source_byte_width)
{
if (!init_rotator(x_scale,&x_rotator,&x_base))
return 0; /* set up x_rotator */
if (!init_rotator(y_scale,&y_rotator,&y_base))
return 0; /* set up y_rotator */
source_width = source_byte_width; /* save width */
if (((long)source_width * (long)x scale) / 100L > FAX_WIDTH)
source_width = FAX_WIDTH; /* clip to fax line */
return 1;
}
/********************************************************************
* function: static int init_rotator(int scale,ROTATOR *rotator,unsigned char *base)
* local function calculates the rotator and basis.
* parameters: scale factor in percent, pointer to rotator,
* pointer to basis.
* returns: 1 if Ok or 0 if scale is out of range *****************************************************************/
static int init_rotator(int scale,ROTATOR *rotator,unsigned char *base)
{
unsigned int index,mix,i;
/* check scaling range */
if (scale < SCALE_MIN ||scale > SCALE_MAX)
return 0;
/* set basis */
*base = scale / 100;
/* get index into pattern */
scale % = 100;
index = (scale * 8) / 100;
/* How we mix 2 consecutive patterns to achieve balance.
* Express it this way to handle round off. */
mix = pattern[((((scale * 8) % 100) + 4) * 8) / 100];
/* for each byte in the rotator, select an appropriate
* pattern byte based on the index and the mix */
*rotator = 0;
for (i = sizeof(ROTATOR) ; i > 0 ; i--)
{
*rotator <<= 8;
*rotator |= pattern[index + (mix & 0x01)];
mix >> = 1;
}
assert((*rotator + *base) != 0);
assert(*base <= PRECISION);
return 1;
}
/*********************************************************************
* function: int scale_fax_line(unsigned char *dest, unsigned char *src)
* Scale the line from src to dest. dest will always be FAX_WIDTH
* wide even if it requires clipping or whiting out the tail. The
* return value indicates the number of times to repeat the line.
* A 0 return means skip it. This was done to keep buffer control in
* the caller. Call scale_fax_line() once for each source line.
* parameters: pointer to destination and source
* returns: number of times to repeat the line (0 to PRECISION) ********************************************************************/
int scale_fax_line(unsigned char *dest, unsigned char *src)
{
int y_repeat,x_repeat,i,bit;
unsigned int accum,byte;
unsigned char *start;
ROTATOR rotator;
/* get line repeat value and rotate to next */
y_repeat = y_base + (y_rotator & 0x01);
_ror(y_rotator);
/* if anything to do */
if (y_repeat)
{
rotator = x_rotator;
for (i=source_width, accum=1, start=dest ; i > 0 ; i--, src++)
{ /* each byte in the source line */
for (bit=8, byte=*src ; bit > 0 ; bit--)
{ /* for each bit in the byte */
x_repeat = x_base + (rotator & 1);
_ror(rotator); /* get repeat count and rotate */
while (x_repeat)--
{ /* for each repeat */
accum <<= 1; /* copy bit into accumulator */
accum += ((byte & 0x80) != 0);
if (accum & 0x100)
{ /* output a byte and reset sentinel */
*dest++ = accum & 0xff;
accum = 1;
}
}
byte <<= 1;
}
}
if (accum > 1)
{ /* handle fragment */
while ((accum & 0x100) == 0)
accum <<= 1;
*dest++ = accum & 0xff;
}
while (dest < start + FAX_WIDTH)
{ /* white out tail */
*dest++ = 0;
}
}
return y_repeat;
}
/* End of File */