/*
 * Copyright (c) 1994 Paul Vojta.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * NOTE:
 *	xdvi is based on prior work as noted in the modification history, below.
 */

/*
 * DVI previewer for X.
 *
 * Eric Cooper, CMU, September 1985.
 *
 * Code derived from dvi-imagen.c.
 *
 * Modification history:
 * 1/1986	Modified for X.10	--Bob Scheifler, MIT LCS.
 * 7/1988	Modified for X.11	--Mark Eichin, MIT
 * 12/1988	Added 'R' option, toolkit, magnifying glass
 *					--Paul Vojta, UC Berkeley.
 * 2/1989	Added tpic support	--Jeffrey Lee, U of Toronto
 * 4/1989	Modified for System V	--Donald Richardson, Clarkson Univ.
 * 3/1990	Added VMS support	--Scott Allendorf, U of Iowa
 * 7/1990	Added reflection mode	--Michael Pak, Hebrew U of Jerusalem
 * 1/1992	Added greyscale code	--Till Brychcy, Techn. Univ. Muenchen
 *					  and Lee Hetherington, MIT
 * 4/1994	Added DPS support, bounding box
 *					--Ricardo Telichevesky
 *					  and Luis Miguel Silveira, MIT RLE.
 */

#include "config.h"

/***
 ***	PK font reading routines.
 ***	Public routines are read_PK_index and read_PK_char.
 ***/

#define PK_ID      89
#define PK_CMD_START 240
#define PK_X1     240
#define PK_X2     241
#define PK_X3     242
#define PK_X4     243
#define PK_Y      244
#define PK_POST   245
#define PK_NOOP   246
#define PK_PRE    247

static	int	PK_flag_byte;
static	unsigned PK_input_byte;
static	int	PK_bitpos;
static	int	PK_dyn_f;
static	int	PK_repeat_count;

static	int
PK_get_nyb(fp)
	register FILE *fp;
{
	unsigned temp;
	if (PK_bitpos < 0) {
	    PK_input_byte = one(fp);
	    PK_bitpos = 4;
	}
	temp = PK_input_byte >> PK_bitpos;
	PK_bitpos -= 4;
	return (temp & 0xf);
}


static	int
PK_packed_num(fp)
	register FILE *fp;
{
	int	i,j;

	if ((i = PK_get_nyb(fp)) == 0) {
	    do {
		j = PK_get_nyb(fp);
		++i;
	    }
	    while (j == 0);
	    while (i > 0) {
		j = (j << 4) | PK_get_nyb(fp);
		--i;
	    }
	    return (j - 15 + ((13 - PK_dyn_f) << 4) + PK_dyn_f);
	}
	else {
	    if (i <= PK_dyn_f) return i;
	    if (i < 14)
		return (((i - PK_dyn_f - 1) << 4) + PK_get_nyb(fp)
		    + PK_dyn_f + 1);
	    if (i == 14) PK_repeat_count = PK_packed_num(fp);
	    else PK_repeat_count = 1;
	    return PK_packed_num(fp);
	}
}


static	void
PK_skip_specials(fontp)
	register struct font *fontp;
{
	int i,j;
	register FILE *fp = fontp->file;

	do {
	    PK_flag_byte = one(fp);
	    if (PK_flag_byte >= PK_CMD_START) {
		switch (PK_flag_byte) {
		    case PK_X1 :
		    case PK_X2 :
		    case PK_X3 :
		    case PK_X4 :
			i = 0;
			for (j = PK_CMD_START; j <= PK_flag_byte; ++j)
			    i = (i << 8) | one(fp);
			while (i--) (void) one(fp);
			break;
		    case PK_Y :
			(void) four(fp);
		    case PK_POST :
		    case PK_NOOP :
			break;
		    default :
			oops("Unexpected %d in PK file %s", PK_flag_byte,
			    fontp->filename);
			break;
		}
	    }
	}
	while (PK_flag_byte != PK_POST && PK_flag_byte >= PK_CMD_START);
}

/*
 *	Public routines
 */

static	void
#if	NeedFunctionPrototypes
read_PK_char(register struct font *fontp, wide_ubyte ch)
#else	/* !NeedFunctionPrototypes */
read_PK_char(fontp, ch)
	register struct font *fontp;
	ubyte	ch;
#endif	/* NeedFunctionPrototypes */
{
	int	i, j;
	int	n;
	int	row_bit_pos;
	Boolean	paint_switch;
	BMUNIT	*cp;
	register struct glyph *g;
	register FILE *fp = fontp->file;
	long	fpwidth;
	BMUNIT	word;
	int	word_weight, bytes_wide;
	int	rows_left, h_bit, count;

	g = &fontp->glyph[ch];
	PK_flag_byte = g->x2;
	PK_dyn_f = PK_flag_byte >> 4;
	paint_switch = ((PK_flag_byte & 8) != 0);
	PK_flag_byte &= 0x7;
	if (PK_flag_byte == 7) n = 4;
	else if (PK_flag_byte > 3) n = 2;
	else n = 1;

	if (debug & DBG_PK) Printf("loading pk char %d, char type %d ", ch, n);

	/*
	 * now read rest of character preamble
	 */
	if (n != 4) fpwidth = num(fp, 3);
	else {
	    fpwidth = sfour(fp);
	    (void) four(fp);	/* horizontal escapement */
	}
	(void) num(fp, n);	/* vertical escapement */
	{
	    unsigned long w, h;

	    w = num(fp, n);
	    h = num(fp, n);
	    if (w > 0x7fff || h > 0x7fff)
		oops("Character %d too large in file %s", ch, fontp->fontname);
	    g->bitmap.w = w;
	    g->bitmap.h = h;
	}
	g->x = snum(fp, n);
	g->y = snum(fp, n);

	g->dvi_adv = fontp->dimconv * fpwidth;

	if (debug & DBG_PK) {
	    if (g->bitmap.w != 0)
		Printf(", size=%dx%d, dvi_adv=%ld", g->bitmap.w, g->bitmap.h,
		    g->dvi_adv);
	    Putchar('\n');
	}

	alloc_bitmap(&g->bitmap);
	cp = (BMUNIT *) g->bitmap.bits;

	/*
	 * read character data into *cp
	 */
	bytes_wide = ROUNDUP((int) g->bitmap.w, BITS_PER_BMUNIT)
	    * BYTES_PER_BMUNIT;
	PK_bitpos = -1;
	if (PK_dyn_f == 14) {	/* get raster by bits */
	    bzero(g->bitmap.bits, (int) g->bitmap.h * bytes_wide);
	    for (i = 0; i < (int) g->bitmap.h; i++) {	/* get all rows */
		cp = ADD(g->bitmap.bits, i * bytes_wide);
#ifndef	MSBITFIRST
		row_bit_pos = -1;
#else
		row_bit_pos = BITS_PER_BMUNIT;
#endif
		for (j = 0; j < (int) g->bitmap.w; j++) {    /* get one row */
		    if (--PK_bitpos < 0) {
			word = one(fp);
			PK_bitpos = 7;
		    }
#ifndef	MSBITFIRST
		    if (++row_bit_pos >= BITS_PER_BMUNIT) {
			cp++;
			row_bit_pos = 0;
		    }
#else
		    if (--row_bit_pos < 0) {
			cp++;
			row_bit_pos = BITS_PER_BMUNIT - 1;
		    }
#endif
		    if (word & (1 << PK_bitpos)) *cp |= 1 << row_bit_pos;
		}
	    }
	}
	else {		/* get packed raster */
	    rows_left = g->bitmap.h;
	    h_bit = g->bitmap.w;
	    PK_repeat_count = 0;
	    word_weight = BITS_PER_BMUNIT;
	    word = 0;
	    while (rows_left > 0) {
		count = PK_packed_num(fp);
		while (count > 0) {
		    if (count < word_weight && count < h_bit) {
#ifndef	MSBITFIRST
			if (paint_switch)
			    word |= bit_masks[count] <<
				(BITS_PER_BMUNIT - word_weight);
#endif
			h_bit -= count;
			word_weight -= count;
#ifdef	MSBITFIRST
			if (paint_switch)
			    word |= bit_masks[count] << word_weight;
#endif
			count = 0;
		    }
		    else if (count >= h_bit && h_bit <= word_weight) {
			if (paint_switch)
			    word |= bit_masks[h_bit] <<
#ifndef	MSBITFIRST
				(BITS_PER_BMUNIT - word_weight);
#else
				(word_weight - h_bit);
#endif
			*cp++ = word;
			/* "output" row(s) */
			for (i = PK_repeat_count * bytes_wide /
				BYTES_PER_BMUNIT; i > 0; --i) {
			    *cp = *SUB(cp, bytes_wide);
			    ++cp;
			}
			rows_left -= PK_repeat_count + 1;
			PK_repeat_count = 0;
			word = 0;
			word_weight = BITS_PER_BMUNIT;
			count -= h_bit;
			h_bit = g->bitmap.w;
		    }
		    else {
			if (paint_switch)
#ifndef	MSBITFIRST
			    word |= bit_masks[word_weight] <<
				(BITS_PER_BMUNIT - word_weight);
#else
			    word |= bit_masks[word_weight];
#endif
			*cp++ = word;
			word = 0;
			count -= word_weight;
			h_bit -= word_weight;
			word_weight = BITS_PER_BMUNIT;
		    }
		}
		paint_switch = 1 - paint_switch;
	    }
	    if (cp != ((BMUNIT *) (g->bitmap.bits + bytes_wide * g->bitmap.h)))
		oops("Wrong number of bits stored:  char. %d, font %s", ch,
		    fontp->fontname);
	    if (rows_left != 0 || h_bit != g->bitmap.w)
		oops("Bad pk file (%s), too many bits", fontp->fontname);
	}
}

void
read_PK_index(fontp, hushcs)
	register struct font	*fontp;
	wide_bool		hushcs;
{
	int	hppp, vppp;
	long	checksum;

	fontp->read_char = read_PK_char;
	if (debug & DBG_PK)
	    Printf("Reading PK pixel file %s\n", fontp->filename);

	Fseek(fontp->file, (long) one(fontp->file), 1);	/* skip comment */

	(void) four(fontp->file);		/* skip design size */
	checksum = four(fontp->file);
	if (!hushcs && checksum && fontp->checksum
	    && checksum != fontp->checksum)
	    Fprintf(stderr,
		"Checksum mismatch (dvi = %lu, pk = %lu) in font file %s\n",
		fontp->checksum, checksum, fontp->filename);
	hppp = sfour(fontp->file);
	vppp = sfour(fontp->file);
	if (hppp != vppp && (debug & DBG_PK))
	    Printf("Font has non-square aspect ratio %d:%d\n", vppp, hppp);
	/*
	 * Prepare glyph array.
	 */
	fontp->glyph = (struct glyph *) xmalloc(256 * sizeof(struct glyph),
	    "glyph array");
	bzero((char *) fontp->glyph, 256 * sizeof(struct glyph));
	/*
	 * Read glyph directory (really a whole pass over the file).
	 */
	for (;;) {
	    int bytes_left, flag_low_bits;
	    unsigned int ch;

	    PK_skip_specials(fontp);
	    if (PK_flag_byte == PK_POST) break;
	    flag_low_bits = PK_flag_byte & 0x7;
	    if (flag_low_bits == 7) {
		bytes_left = four(fontp->file);
		ch = four(fontp->file);
	    } else if (flag_low_bits > 3) {
		bytes_left = ((flag_low_bits - 4) << 16) + two(fontp->file);
		ch = one(fontp->file);
	    } else {
		bytes_left = (flag_low_bits << 8) + one(fontp->file);
		ch = one(fontp->file);
	    }
	    fontp->glyph[ch].addr = ftell(fontp->file);
	    fontp->glyph[ch].x2 = PK_flag_byte;
#ifdef linux
#ifndef SHORTSEEK
#define SHORTSEEK 1024
#endif
          /* A bug in Linux libc (as of 18oct94) makes a short read faster
             than a short forward seek. Totally non-intuitive.  */
          if (bytes_left > 0 && bytes_left < SHORTSEEK) {
            char *dummy = xmalloc (bytes_left, "shortseek");
            Fread (dummy, 1, bytes_left, fontp->file);
            free (dummy);
          } else
            /* seek backward, or long forward */
#endif /* linux */
	    Fseek(fontp->file, (long) bytes_left, 1);
	    if (debug & DBG_PK)
		Printf("Scanning pk char %u, at %ld.\n", ch,
		    fontp->glyph[ch].addr);
	}
}
