/*
 * bitmaps.c - varios bitmap utilities (b&w images)
 *
 * $Log: bitmaps.c,v $
 * Revision 1.13  1994/06/01  22:25:52  clm
 * fixed memcpy calls so images are displayed correctly
 *
 * Revision 1.12  1994/05/04  23:34:36  clm
 * ported to ANSI C
 *
 * Revision 1.11  1992/09/11  15:50:49  clm
 * copied Kevin's version with IMAGE_DIRECTORY to xopps
 *
 * Revision 1.5  1991/09/12  03:57:45  kevin
 * added IMAGE_DIRECTORY
 *
 * Revision 1.4  1991/07/09  17:07:14  chester
 * added function headers
 *
 * Revision 1.3  1991/05/31  21:55:13  chester
 * replace free and malloc with X toolkit XtFree and XtMalloc.
 * added void typecasts to standard C functions.
 * added return statement to scale bitmap.
 *
 * Revision 1.2  1991/03/14  19:25:14  chester
 * no change... just a mistake.
 *
 * Revision 1.1  1991/01/17  16:57:34  chester
 * Initial revision
 *
 *
 * Derived from xgened bitmaps.c 1.4
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <memory.h>
#include <Xm/Xm.h>

#include "xopps.h"

#ifndef lint
static char rcsid[] =
    "$Id: bitmaps.c,v 1.13 1994/06/01 22:25:52 clm OEL $";
#endif

#define get_pixel(data, rowlen, x, y) (*((data) + (y) * (rowlen) + (x) / 8) \
	& (1 << ((x) % 8)))
#define set_pixel(data, rowlen, x, y) *((data) + (y) * (rowlen) + (x) / 8) \
	|= (1 << ((x) % 8))

/* local function declarations */
static GRBitmap *read_X11_bitmap(FILE *);

/***********************************************************************
 *
 * FUNCTION:
 *	open_bitmap()
 *
 * INPUTS:
 *	filename	- (char*) name of bitmap file
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	reads various format b&w bitmaps from file
 *
 */

GRBitmap *
open_bitmap(char *filename)
{
    FILE *fp;			/* file pointer */
    GRBitmap *bm;		/* return image */
    char newpath[1024];		/* full pathname with IMAGE_DIRECTORY */
    char *tp;			/* value of IMAGE_DIRECTORY */

    if ((filename != NULL) && (filename[0] != '\0')) {
	if ((fp = fopen(filename, "r")) == NULL) {
	    if (filename[0] != '/') {
		tp = getenv("IMAGE_DIRECTORY");
		if (tp != NULL) {
		    strcpy(newpath, tp);
		    strcat(newpath, "/");
		    strcat(newpath, filename);
		}
		fp = fopen(newpath, "r");
	    }
	}
    } else {
	fp = NULL;
    }

    if (fp != NULL) {
	bm = read_X11_bitmap(fp);
	(void)fclose(fp);
    } else {
	bm = NULL;
    }
    return bm;
}

/***********************************************************************
 *
 * FUNCTION:
 *	read_X11_bitmap()
 *
 * INPUTS:
 *	fp	- (FILE *) file pointer to the bitmap file
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	reads X11 bitmap data file
 *
 */

static GRBitmap *
read_X11_bitmap(FILE *fp)
{
    GRBitmap *bm;			/* returned bitmap */
    char buf[128], *ptr;		/* input buffer */
    int width, height;			/* size of image */
    unsigned long length;		/* length of bitmap in bytes */

    bm = (GRBitmap *)XtMalloc(sizeof(GRBitmap));
    bm->bits = NULL;

    /* look for #define *_width */
    while (1) {
	if (fgets(buf, 128, fp) == NULL) goto bad_file;
	if (strncmp(buf, "#define", 7)) continue;
	if ((ptr = strstr(buf + 7, "_width")) == NULL) continue;
	width = atoi(ptr + 6);
	if ((width <= 0) || (width > 65535)) goto bad_file;
	bm->width = (unsigned short)width;
	break;
    }

    /* look for #define *_height */
    while (1) {
	if (fgets(buf, 128, fp) == NULL) goto bad_file;
	if (strncmp(buf, "#define", 7)) continue;
	if ((ptr = strstr(buf + 7, "_height")) == NULL) continue;
	height = atoi(ptr + 7);
	if ((height <= 0) || (height > 65535)) goto bad_file;
	bm->height = (unsigned short)height;
	break;
    }

    /* skip over header */
    while (1) {
	if (fgets(buf, 128, fp) == NULL) goto bad_file;
	if (strncmp(buf, "static char", 11)) continue;
	if (strstr(buf + 11, "_bits[] = {") == NULL) continue;
	break;
    }

    /* calculate length of bitmap in bytes and allocate memory */
    length = height * ((width - 1) / 8 + 1);
    ptr = bm->bits = XtMalloc(length);

    /* read in bitmap */
    while (length--) {
	if (fscanf(fp, "%s", buf) != 1) goto bad_file;
	if (buf[0] != '0') goto bad_file;
	if (buf[1] != 'x') goto bad_file;
	if (!isxdigit(buf[2])) goto bad_file;
	if (!isxdigit(buf[3])) goto bad_file;
	if ((length) && (buf[4] != ',')) goto bad_file;
	*ptr = (buf[2] - (isupper(buf[2]) ? ('A' - 10) :
	    (islower(buf[2]) ? ('a' - 10) : '0'))) << 4;
	*ptr |= (buf[3] - (isupper(buf[3]) ? ('A' - 10) :
	    (islower(buf[3]) ? ('a' - 10) : '0')));
	++ptr;
    }

    /* everything was ok */
    return bm;

    /* remove bitmap structure and quit on error */
bad_file:
    if (bm->bits != NULL) XtFree(bm->bits);
    XtFree((char *)bm);
    return NULL;
}

/***********************************************************************
 *
 * FUNCTION:
 *	copy_bitmap()
 *
 * INPUTS:
 *	bm1	- (GRBitmap *) Pointer to Bitmap structure
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *
 */

GRBitmap *
copy_bitmap(GRBitmap *bm1)
{
    GRBitmap *bm2;
    Cardinal length;

    if (bm1 == NULL) {
	bm2 = NULL;
    } else {
	bm2 = (GRBitmap *)XtMalloc(sizeof(GRBitmap));
	bm2->width = bm1->width;
	bm2->height = bm1->height;
	length = bm1->height * ((bm1->width - 1) / 8 + 1);
	bm2->bits = (char *)XtMalloc(length);
	memcpy(bm2->bits, bm1->bits, length);
    }
    return bm2;
}

/***********************************************************************
 *
 * FUNCTION:
 *	scale_bitmap()
 *
 * INPUTS:
 *	bm1	- (GRBitmap *) pointer to the gr_routine bitmap structure
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *
 */

GRBitmap *
scale_bitmap(GRBitmap *bm1, int scale)
{
    GRBitmap *bm2;		/* reduced bitmap */
    Cardinal length;		/* length of reduced bitmap */
    int rowlen1, rowlen2;	/* row length of each bitmap */
    int x, y, i, j, x1, y1;	/* coordinates */
    int count;			/* count of pixels turned on */
    int total;			/* total number of pixels in this sub-area */
    int threshold;		/* threshold for turning new pixel on */
    int area;			/* area of old bitmap */

    if (bm1 == NULL) {
	bm2 = NULL;
    } else if (scale == 1) {
	bm2 = (GRBitmap *)XtMalloc(sizeof(GRBitmap));
	bm2->width = bm1->width;
	bm2->height = bm1->height;
	length = bm1->height * ((bm1->width - 1) / 8 + 1);
	bm2->bits = (char *)XtMalloc(length);
	memcpy(bm2->bits, bm1->bits, length);
    } else {
	bm2 = (GRBitmap *)XtMalloc(sizeof(GRBitmap));
	bm2->width = (bm1->width - 1) / scale + 1;
	bm2->height = (bm1->height - 1) / scale + 1;
	length = bm2->height * ((bm2->width - 1) / 8 + 1);
	bm2->bits = (char *)XtMalloc(length);
	memset(bm2->bits, '\0', length);
	rowlen1 = (bm1->width - 1) / 8 + 1;
	rowlen2 = (bm2->width - 1) / 8 + 1;

	threshold = 0;				/* find threshold  */
	for (x = 0; x < bm1->width; x++) {
	    for (y = 0; y < bm1->height; y++) {
		if (get_pixel(bm1->bits, rowlen1, x, y)) threshold++;
	    }
	}
	area = bm1->width * bm1->height;

	for (x = 0; x < bm2->width; x++) {	/* calculate new points  */
	    for (y = 0; y < bm2->height; y++) {
		count = 0;
		total = 0;
		for (i = 0; i < scale; i++) {
		    x1 = scale * x + i;
		    if (x1 < bm1->width) {
			for (j = 0; j < scale; j++) {
			    y1 = scale * y + j;
			    if (y1 < bm1->height) {
				++total;
				if (get_pixel(bm1->bits, rowlen1, x1, y1))
				    count++;
			    }
			}
		    }
		}
		if (count * area > threshold * total)
		    set_pixel(bm2->bits, rowlen2, x, y);
	    }
	}
    }
    return bm2;
}
