/*
 * The Python Imaging Library
 * $Id$
 *
 * histogram support
 *
 * history:
 * 1995-06-15 fl   Created.
 * 1996-04-05 fl   Fixed histogram for multiband images.
 * 1997-02-23 fl   Added mask support
 * 1998-07-01 fl   Added basic 32-bit float/integer support
 *
 * Copyright (c) 1997-2003 by Secret Labs AB.
 * Copyright (c) 1995-2003 by Fredrik Lundh.
 *
 * See the README file for information on usage and redistribution.
 */


#include "Imaging.h"


/* HISTOGRAM */
/* --------------------------------------------------------------------
 * Take a histogram of an image. Returns a histogram object containing
 * 256 slots per band in the input image.
 */

void
ImagingHistogramDelete(ImagingHistogram h)
{
    if (h->histogram)
	free(h->histogram);
    free(h);
}

ImagingHistogram
ImagingHistogramNew(Imaging im)
{
    ImagingHistogram h;

    /* Create histogram descriptor */
    h = calloc(1, sizeof(struct ImagingHistogramInstance));
    strncpy(h->mode, im->mode, IMAGING_MODE_LENGTH-1);
    h->mode[IMAGING_MODE_LENGTH-1] = 0;

    h->bands = im->bands;
    h->histogram = calloc(im->pixelsize, 256 * sizeof(long));

    return h;
}

ImagingHistogram
ImagingGetHistogram(Imaging im, Imaging imMask, void* minmax)
{
    ImagingSectionCookie cookie;
    int x, y, i;
    ImagingHistogram h;
    INT32 imin, imax;
    FLOAT32 fmin, fmax, scale;

    if (!im)
	return ImagingError_ModeError();

    if (imMask) {
	/* Validate mask */
	if (im->xsize != imMask->xsize || im->ysize != imMask->ysize)
	    return ImagingError_Mismatch();
	if (strcmp(imMask->mode, "1") != 0 && strcmp(imMask->mode, "L") != 0)
	    return ImagingError_ValueError("bad transparency mask");
    }

    h = ImagingHistogramNew(im);

    if (imMask) {
	/* mask */
	if (im->image8) {
            ImagingSectionEnter(&cookie);
	    for (y = 0; y < im->ysize; y++)
		for (x = 0; x < im->xsize; x++)
		    if (imMask->image8[y][x] != 0)
			h->histogram[im->image8[y][x]]++;
            ImagingSectionLeave(&cookie);
	} else { /* yes, we need the braces. C isn't Python! */
            if (im->type != IMAGING_TYPE_UINT8) {
                ImagingHistogramDelete(h);
                return ImagingError_ModeError();
            }
            ImagingSectionEnter(&cookie);
	    for (y = 0; y < im->ysize; y++) {
		UINT8* in = (UINT8*) im->image32[y];
		for (x = 0; x < im->xsize; x++)
		    if (imMask->image8[y][x] != 0) {
			h->histogram[(*in++)]++;
			h->histogram[(*in++)+256]++;
			h->histogram[(*in++)+512]++;
			h->histogram[(*in++)+768]++;
		    } else
			in += 4;
	    }
            ImagingSectionLeave(&cookie);
	}
    } else {
	/* mask not given; process pixels in image */
	if (im->image8) {
            ImagingSectionEnter(&cookie);
	    for (y = 0; y < im->ysize; y++)
		for (x = 0; x < im->xsize; x++)
		    h->histogram[im->image8[y][x]]++;
            ImagingSectionLeave(&cookie);
	} else {
            switch (im->type) {
            case IMAGING_TYPE_UINT8:
                ImagingSectionEnter(&cookie);
                for (y = 0; y < im->ysize; y++) {
                    UINT8* in = (UINT8*) im->image[y];
                    for (x = 0; x < im->xsize; x++) {
                        h->histogram[(*in++)]++;
                        h->histogram[(*in++)+256]++;
                        h->histogram[(*in++)+512]++;
                        h->histogram[(*in++)+768]++;
                    }
                }
                ImagingSectionLeave(&cookie);
                break;
            case IMAGING_TYPE_INT32:
                if (!minmax) {
                    ImagingHistogramDelete(h);
                    return ImagingError_ValueError("min/max not given");
                }
                if (!im->xsize || !im->ysize)
                    break;
                memcpy(&imin, minmax, sizeof(imin));
                memcpy(&imax, ((char*)minmax) + sizeof(imin), sizeof(imax));
                if (imin >= imax)
                    break;
                ImagingSectionEnter(&cookie);
                scale = 255.0F / (imax - imin);
                for (y = 0; y < im->ysize; y++) {
                    INT32* in = im->image32[y];
                    for (x = 0; x < im->xsize; x++) {
                        i = (int) (((*in++)-imin)*scale);
                        if (i >= 0 && i < 256)
                            h->histogram[i]++;
                    }
                }
                ImagingSectionLeave(&cookie);
                break;
            case IMAGING_TYPE_FLOAT32:
                if (!minmax) {
                    ImagingHistogramDelete(h);
                    return ImagingError_ValueError("min/max not given");
                }
                if (!im->xsize || !im->ysize)
                    break;
                memcpy(&fmin, minmax, sizeof(fmin));
                memcpy(&fmax, ((char*)minmax) + sizeof(fmin), sizeof(fmax));
                if (fmin >= fmax)
                    break;
                ImagingSectionEnter(&cookie);
                scale = 255.0F / (fmax - fmin);
                for (y = 0; y < im->ysize; y++) {
                    FLOAT32* in = (FLOAT32*) im->image32[y];
                    for (x = 0; x < im->xsize; x++) {
                        i = (int) (((*in++)-fmin)*scale);
                        if (i >= 0 && i < 256)
                            h->histogram[i]++;
                    }
                }
                ImagingSectionLeave(&cookie);
                break;
            }
        }
    }

    return h;
}
