#ifndef lint
static char rcsid[] = "$Header: /ful/chris/ctex_dist/lib/RCS/scaletfm.c,v 1.2 84/05/29 02:19:33 chris Exp $";
#endif

#include "types.h"
#include "pxl.h"

/* From DVITYPE.WEB:

 ``The most important part of in_TFM is the width computation, which
   involvles multiplying the relative widths in the TFM file by the scaling
   factor in the DVI file.  This fixed-point multiplication must be done with
   precisely the same accuracy by all DVI-reading programs, in order to
   validate the assumptions made by DVI-writing programs like \TeX 82.

   Let us therefore summarize what needs to be done.  Each width in a TFM
   file appears as a four-byte quantity called a fix_word.  A fix_word whose
   respective bytes are (a,b,c,d) represents the number

	   {{ b * 2^{-4} + c * 2^{-12} + d * 2^{-20},        if a = 0;
      x = {{
	   {{ -16 + b * 2^{-4} + c * 2^{-12} + d * 2^{-20},  if a = 255.

   (No other choices of a are allowed, since the magnitude of a TFM dimension
   must be less than 16.)  We want to multiply this quantity by the integer
   z, which is known to be less than 2^{27}.  Let \alpha = 16z.  If z <
   2^{23}, the individual multiplications b * z, c * z, d * z cannot
   overflow; otherwise we will divide z by 2, 4, 8, or 16, to obtain a
   multiplier less than 2^{23}, and we can compensate for this later.  If z
   has thereby been replaced by z' = z/2^e, let \beta = 2^{4-e}; we shall
   compute

	\lfloor (b + c * 2^{-8} + d * 2^{-16})z' / \beta \rfloor

   if a = 0, or the same quantity minus \alpha if a = 255.  This calculation
   must be done exactly, for the reasons stated above; the following program
   does the job in a system-independent way, assuming that arithmetic is
   exact on numbers less than 2^{31} in magnitude.'' */

/* In other words, we are assuming 32-bit (minimum) arithmetic, and take
   pains to ensure that each intermediate result fits within 32 bits.
   This routine converts the TFM widths in the px_info part of a pxltail
   pointer 'px' given the scale factor 'z'. */
ScaleTFMWidths (px, z)
struct pxltail *px;
register i32 z;
{
    register i32    alpha,
		    log2beta,
		    t;
    register struct chinfo *ch;
    register int    i;

 /* First compute \alpha, \beta, and z': */
    alpha = 16 * z;
    log2beta = 4;
    while (z >= (1 << 23)) {
	z >>= 1;
	log2beta--;
    }

 /* The four values 'a', 'b', 'c', and 'd' are fields within t: */
#define a (UnSign8 (t >> 24))
#define b (UnSign8 (t >> 16))
#define c (UnSign8 (t >> 8))
#define d (UnSign8 (t))

    ch = px -> px_info;
    for (i = 0; i < 256; i++) {
	if (t = ch -> ch_TFMwidth) {
	    t = (((((d * z) >> 8) + c * z) >> 8) + b * z) >> log2beta;
	    if (a) {
		if (a != 255)
		    error (0, 0, "bad TFM width!");
		t -= alpha;
	    }
	    ch -> ch_TFMwidth = t;
	}
	ch++;
    }
}

/* Provided in case anyone is not using the standard PXL file formats:
   scale the single TFM width 't' by 'z' */
i32
ScaleOneWidth (t, z)
register i32 t, z;
{
    register i32    alpha,
		    log2beta;

    alpha = 16 * z;
    log2beta = 4;
    while (z >= (1 << 23)) {
	z >>= 1;
	log2beta--;
    }

    if (t) {
	t = (((((d * z) >> 8) + c * z) >> 8) + b * z) >> log2beta;
	if (a) {
	    if (a != 255)
		error (0, 0, "bad TFM width! [ScaleOneWidth]");
	    t -= alpha;
	}
    }

    return t;
}
