/* find histograms
 *
 * Copyright: 1990, 1991, N. Dessipris.
 *
 * Author: Nicos Dessipris.
 * Written on: 09/07/1990
 * Modified on : 11/03/1991
 * 19/7/93 JC
 *	- test for Coding type added
 * 26/10/94 JC
 *	- rewritten for ANSI
 *	- now does USHORT too
 *	- 5 x faster!
 * 2/6/95 JC
 *	- rewritten for partials
 * 3/3/01 JC
 *	- tiny speed ups
 * 21/1/07
 * 	- number bands from zero
 * 24/3/10
 * 	- gtkdoc
 * 	- small celanups
 */

/*

    This file is part of VIPS.
    
    VIPS is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 */

/*

    These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk

 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <vips/vips.h>

#ifdef WITH_DMALLOC
#include <dmalloc.h>
#endif /*WITH_DMALLOC*/

/* Accumulate a histogram in one of these.
 */
typedef struct {
	int bands;		/* Number of bands in output */
	int which;		/* If one band in out, which band of input */
	int size;		/* Length of bins */
	int mx;			/* Maximum value we have seen */
	unsigned int **bins;	/* All the bins! */
} Histogram;

/* Build a Histogram.
 */
static Histogram *
build_hist( IMAGE *out, int bands, int which, int size )
{
	int i;
	Histogram *hist = IM_NEW( out, Histogram );

	if( !hist || !(hist->bins = IM_ARRAY( out, bands, unsigned int * )) )
		return( NULL );
	for( i = 0; i < bands; i++ ) {
		if( !(hist->bins[i] = IM_ARRAY( out, size, unsigned int )) )
			return( NULL );
		memset( hist->bins[i], 0, size * sizeof( unsigned int ) );
	}

	hist->bands = bands;
	hist->which = which;
	hist->size = size;
	hist->mx = 0;

	return( hist );
}

/* Build a sub-hist, based on the main hist.
 */
static void *
build_subhist( IMAGE *out, void *a, void *b )
{
	Histogram *mhist = (Histogram *) a;

	return( (void *) 
		build_hist( out, mhist->bands, mhist->which, mhist->size ) );
}

/* Join a sub-hist onto the main hist.
 */
static int
merge_subhist( void *seq, void *a, void *b )
{
	Histogram *shist = (Histogram *) seq;
	Histogram *mhist = (Histogram *) a;
	int i, j;

	g_assert( shist->bands == mhist->bands && shist->size == mhist->size );

	/* Add on sub-data.
	 */
	mhist->mx = IM_MAX( mhist->mx, shist->mx );
	for( i = 0; i < mhist->bands; i++ )
		for( j = 0; j < mhist->size; j++ )
			mhist->bins[i][j] += shist->bins[i][j];

	/* Blank out sub-hist to make sure we can't add it again.
	 */
	shist->mx = 0;
	for( i = 0; i < shist->bands; i++ )
		shist->bins[i] = NULL;
	
	return( 0 );
}

/* Histogram of all bands of a uchar image.
 */
static int
find_uchar_hist( REGION *reg, void *seq, void *a, void *b )
{
	Histogram *hist = (Histogram *) seq;
	Rect *r = &reg->valid;
	IMAGE *im = reg->im;
	int le = r->left;
	int to = r->top;
	int bo = IM_RECT_BOTTOM(r);
	int nb = im->Bands;
	int x, y, z;

	/* Accumulate!
	 */
	for( y = to; y < bo; y++ ) {
		PEL *p = (PEL *) IM_REGION_ADDR( reg, le, y );
		int i;

		for( i = 0, x = 0; x < r->width; x++ )
			for( z = 0; z < nb; z++, i++ )
				hist->bins[z][p[i]]++;
	}

	/* Note the maximum.
	 */
	hist->mx = 255;

	return( 0 );
}

/* Histogram of a selected band of a uchar image.
 */
static int
find_uchar_hist_extract( REGION *reg, void *seq, void *a, void *b )
{
	Histogram *hist = (Histogram *) seq;
	Rect *r = &reg->valid;
	IMAGE *im = reg->im;
	int le = r->left;
	int to = r->top;
	int bo = IM_RECT_BOTTOM(r);
	unsigned int *bins = hist->bins[0];
	int nb = im->Bands;
	int max = r->width * nb;
	int x, y;

	/* Accumulate!
	 */
	for( y = to; y < bo; y++ ) {
		PEL *p = (PEL *) IM_REGION_ADDR( reg, le, y );

		for( x = hist->which; x < max; x += nb ) 
			bins[p[x]]++;
	}

	/* Note the maximum.
	 */
	hist->mx = 255;

	return( 0 );
}

/* Histogram of all bands of a ushort image.
 */
static int
find_ushort_hist( REGION *reg, void *seq, void *a, void *b )
{
	Histogram *hist = (Histogram *) seq;
	Rect *r = &reg->valid;
	IMAGE *im = reg->im;
	int le = r->left;
	int to = r->top;
	int bo = IM_RECT_BOTTOM(r);
	int mx = hist->mx;
	int nb = im->Bands;
	int x, y, z;

	/* Accumulate!
	 */
	for( y = to; y < bo; y++ ) {
		unsigned short *p = (unsigned short *) 
			IM_REGION_ADDR( reg, le, y );
		int i;

		for( i = 0, x = 0; x < r->width; x++ )
			for( z = 0; z < nb; z++, i++ ) {
				int v = p[i];

				/* Adjust maximum.
				 */
				if( v > mx )
					mx = v;

				hist->bins[z][v]++;
			}
	}

	/* Note the maximum.
	 */
	hist->mx = mx;

	return( 0 );
}

/* Histogram of all bands of a ushort image.
 */
static int
find_ushort_hist_extract( REGION *reg, void *seq, void *a, void *b )
{
	Histogram *hist = (Histogram *) seq;
	Rect *r = &reg->valid;
	IMAGE *im = reg->im;
	int le = r->left;
	int to = r->top;
	int bo = IM_RECT_BOTTOM(r);
	int mx = hist->mx;
	unsigned int *bins = hist->bins[0];
	int nb = im->Bands;
	int max = nb * r->width;
	int x, y;

	/* Accumulate!
	 */
	for( y = to; y < bo; y++ ) {
		unsigned short *p = (unsigned short *) 
			IM_REGION_ADDR( reg, le, y ) + hist->which;

		for( x = hist->which; x < max; x += nb ) {
			int v = p[x];

			/* Adjust maximum.
			 */
			if( v > mx )
				mx = v;

			bins[v]++;
		}
	}

	/* Note the maximum.
	 */
	hist->mx = mx;

	return( 0 );
}

/**
 * im_histgr:
 * @in: input image
 * @out: output image
 * @bandno: band to equalise
 *
 * Find the histogram of @in. Find the histogram for band @bandno (producing a
 * one-band histogram), or for all bands (producing an n-band histogram) if 
 * @bandno is -1. 
 *
 * @in must be u8 or u16. @out is always u32.
 *
 * See also: im_hist_indexed(), im_histeq().
 *
 * Returns: 0 on success, -1 on error
 */
int 
im_histgr( IMAGE *in, IMAGE *out, int bandno )
{
	int size;		/* Length of hist */
	int bands;		/* Number of bands in output */
	Histogram *mhist;
	im_generate_fn scanfn;
	int i, j;
	unsigned int *obuffer, *q;

	/* Check images. PIO from in, WIO to out.
	 */
	if( im_check_uncoded( "im_histgr", in ) || 
		im_check_u8or16( "im_histgr", in ) ||
		im_check_bandno( "im_histgr", in, bandno ) ||
		im_pincheck( in ) || 
		im_outcheck( out ) )
		return( -1 );

	/* Find the range of pixel values we must handle.
	 */
	size = in->BandFmt == IM_BANDFMT_UCHAR ? 256 : 65536;

	/* How many output bands?
	 */
	if( bandno == -1 ) 
		bands = in->Bands;
	else 
		bands = 1;

	/* Build main hist we accumulate data in.
	 */
	if( !(mhist = build_hist( out, bands, bandno, size )) )
		return( -1 );

	/* Select scan function.
	 */
	if( in->BandFmt == IM_BANDFMT_UCHAR && bandno == -1 ) 
		scanfn = find_uchar_hist;
	else if( in->BandFmt == IM_BANDFMT_UCHAR )
		scanfn = find_uchar_hist_extract;
	else if( in->BandFmt == IM_BANDFMT_USHORT && bandno == -1 )
		scanfn = find_ushort_hist;
	else
		scanfn = find_ushort_hist_extract;

	/* Accumulate data.
	 */
	if( vips_sink( in, 
		build_subhist, scanfn, merge_subhist, mhist, NULL ) )
		return( -1 );

	/* Make the output image.
	 */
	if( im_cp_desc( out, in ) ) 
		return( -1 );
	im_initdesc( out,
		mhist->mx + 1, 1, bands, IM_BBITS_INT, IM_BANDFMT_UINT, 
		IM_CODING_NONE, IM_TYPE_HISTOGRAM, 1.0, 1.0, 0, 0 );
	if( im_setupout( out ) )
		return( -1 );

	/* Interleave for output.
	 */
	if( !(obuffer = IM_ARRAY( out, 
		IM_IMAGE_N_ELEMENTS( out ), unsigned int )) )
		return( -1 );
	for( q = obuffer, j = 0; j < out->Xsize; j++ )
		for( i = 0; i < out->Bands; i++ )
			*q++ = mhist->bins[i][j];

	/* Write interleaved buffer into hist.
	 */
	if( im_writeline( 0, out, (PEL *) obuffer ) )
		return( -1 );

	return( 0 );
}
