/*
 * 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) {
        if (h->histogram) {
            free(h->histogram);
        }
        free(h);
    }
}

ImagingHistogram
ImagingHistogramNew(Imaging im) {
    ImagingHistogram h;

    /* Create histogram descriptor */
    h = calloc(1, sizeof(struct ImagingHistogramInstance));
    if (!h) {
        return (ImagingHistogram)ImagingError_MemoryError();
    }
    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));
    if (!h->histogram) {
        free(h);
        return (ImagingHistogram)ImagingError_MemoryError();
    }

    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 (!h) {
        return NULL;
    }

    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;
}
