/**************************************************************

konvoes.c (C-Munipack project)
Conversion functions for OES Astro files
Copyright (C) 2003 David Motl, dmotl@volny.cz

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU 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 General Public License for more details.

You should have received a copy of the GNU 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.

**************************************************************/

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

#include "image.h"
#include "comfun.h"
#include "robmean.h"
#include "ccdfile.h"
#include "image.h"
#include "cmpack_image.h"

#define S(x,y)	src[(x)+width*(y)]
#define D(x,y)	dst[(x)+width*(y)]

/* Compute number of bytes that are required to store the image */
int cmpack_image_size(int nx, int ny, CmpackBitpix bitpix)
{
	switch (bitpix)
	{
	case CMPACK_BITPIX_SSHORT:
	case CMPACK_BITPIX_USHORT:
		return nx*ny*sizeof(int16_t);
	case CMPACK_BITPIX_SLONG:
	case CMPACK_BITPIX_ULONG:
		return nx*ny*sizeof(int32_t);
	case CMPACK_BITPIX_FLOAT:
		return nx*ny*sizeof(float);
	case CMPACK_BITPIX_DOUBLE:
		return nx*ny*sizeof(double);
	default:
		return 0;
	}
}

/* Create a new image */
CmpackImage *cmpack_image_new(int width, int height, CmpackBitpix format)
{
	CmpackImage *img;

	if (width<0 || height<0 || width>65535 || height>65535)
		return NULL;
	if (width>0 && height>0 && format==CMPACK_BITPIX_AUTO)
		return NULL;
	
	img = (CmpackImage*)cmpack_calloc(1, sizeof(CmpackImage));
	img->refcnt = 1;
	img->width = width;
	img->height = height;
	img->format = format;
	img->internal_buffer = 1;
	img->datalen = cmpack_image_size(width, height, format);
	img->data = cmpack_malloc(img->datalen);
	return img;
}

/* Create a new image */
CmpackImage *cmpack_image_from_data(int width, int height, CmpackBitpix format, 
	void *data, int datalen)
{
	CmpackImage *img;

	if (width<0 || height<0 || width>65535 || height>65535)
		return NULL;
	if (format==CMPACK_BITPIX_AUTO)
		return NULL;
	if (!data || cmpack_image_size(width, height, format) > datalen)
		return NULL;
	
	img = (CmpackImage*)cmpack_calloc(1, sizeof(CmpackImage));
	img->refcnt = 1;
	img->width = width;
	img->height = height;
	img->format = format;
	img->internal_buffer = 0;
	img->datalen = datalen;
	img->data = data;
	return img;
}

/* Create a new empty image */
CmpackImage *cmpack_image_new_empty(void)
{
	CmpackImage *img = (CmpackImage*)cmpack_calloc(1, sizeof(CmpackImage));
	img->refcnt = 1;
	return img;
}

/* Make a new reference to the CCD image */
CmpackImage *cmpack_image_reference(CmpackImage *img)
{
	img->refcnt++;
	return img;
}

/* Release a reference to the CCD image */
void cmpack_image_destroy(CmpackImage *img)
{
	if (img) {
		img->refcnt--;
		if (img->refcnt==0) {
			if (img->internal_buffer)
				cmpack_free(img->data);
			cmpack_free(img);
		}
	}
}

/* Make copy of the image */
CmpackImage *cmpack_image_copy(const CmpackImage *src)
{
	if (src) {
		CmpackImage *dst = cmpack_image_new(src->width, src->height, src->format);
		if (dst)
			memcpy(dst->data, src->data, dst->datalen);
		return dst;
	}
	return NULL;
}

/* Get image data format */
CmpackBitpix cmpack_image_bitpix(const CmpackImage *img)
{
	return img->format;
}

/* Get width of the image */
int cmpack_image_width(const CmpackImage *img)
{
	return img->width;
}

/* Get height of the image */
int cmpack_image_height(const CmpackImage *img)
{
	return img->height;
}

/* Get pointer to the image data */
void *cmpack_image_data(CmpackImage *img)
{
	return img->data;
}

/* Get pointer to the image data */
const void *cmpack_image_const_data(const CmpackImage *img)
{
	return img->data;
}

static int image_convert(const void *src, int width, int height, CmpackBitpix format,
	void *dst, int dst_size, CmpackBitpix dst_format)
{
	int i, pixels;

	pixels = width*height;
	switch (format) 
	{
	case CMPACK_BITPIX_SSHORT:
		switch (dst_format)
		{
		case CMPACK_BITPIX_SSHORT:
			memcpy(dst, src, pixels*sizeof(int16_t));
			break;
		case CMPACK_BITPIX_USHORT:
			{
				int16_t *sptr = (int16_t*)src;
				uint16_t *dptr = (uint16_t*)dst;
				for (i=0; i<pixels; i++) {
					int16_t val = *sptr++;
					if (val<=0)
						*dptr++ = 0;
					else
						*dptr++ = val;
				}
			}
			break;
		case CMPACK_BITPIX_SLONG:
			{
				int16_t *sptr = (int16_t*)src;
				int32_t *dptr = (int32_t*)dst;
				for (i=0; i<pixels; i++) 
					*dptr++ = *sptr++;
			}
			break;
		case CMPACK_BITPIX_ULONG:
			{
				int16_t *sptr = (int16_t*)src;
				uint32_t *dptr = (uint32_t*)dst;
				for (i=0; i<pixels; i++) {
					int16_t val = *sptr++;
					if (val<=0)
						*dptr++ = 0;
					else
						*dptr++ = val;
				}
			}
			break;
		case CMPACK_BITPIX_FLOAT:
			{
				int16_t *sptr = (int16_t*)src;
				float *dptr = (float*)dst;
				for (i=0; i<pixels; i++) 
					*dptr++ = (float)(*sptr++);
			}
			break;
		case CMPACK_BITPIX_DOUBLE:
			{
				int16_t *sptr = (int16_t*)src;
				double *dptr = (double*)dst;
				for (i=0; i<pixels; i++) 
					*dptr++ = (double)(*sptr++);
			}
			break;
		default:
			return CMPACK_ERR_UNKNOWN_FORMAT;
		}
		break;

	case CMPACK_BITPIX_USHORT:
		switch (dst_format)
		{
		case CMPACK_BITPIX_SSHORT:
			{
				uint16_t *sptr = (uint16_t*)src;
				int16_t *dptr = (int16_t*)dst;
				for (i=0; i<pixels; i++) {
					uint16_t val = *sptr++;
					if (val>=INT16_MAX)
						*dptr++ = INT16_MAX;
					else
						*dptr++ = (int16_t)val;
				}
			}
			break;
		case CMPACK_BITPIX_USHORT:
			memcpy(dst, src, pixels*sizeof(uint16_t));
			break;
		case CMPACK_BITPIX_SLONG:
			{
				uint16_t *sptr = (uint16_t*)src;
				int32_t *dptr = (int32_t*)dst;
				for (i=0; i<pixels; i++) 
					*dptr++ = (*sptr++);
			}
			break;
		case CMPACK_BITPIX_ULONG:
			{
				uint16_t *sptr = (uint16_t*)src;
				uint32_t *dptr = (uint32_t*)dst;
				for (i=0; i<pixels; i++) 
					*dptr++ = (*sptr++);
			}
			break;
		case CMPACK_BITPIX_FLOAT:
			{
				uint16_t *sptr = (uint16_t*)src;
				float *dptr = (float*)dst;
				for (i=0; i<pixels; i++) 
					*dptr++ = (float)(*sptr++);
			}
			break;
		case CMPACK_BITPIX_DOUBLE:
			{
				uint16_t *sptr = (uint16_t*)src;
				double *dptr = (double*)dst;
				for (i=0; i<pixels; i++) 
					*dptr++ = (double)(*sptr++);
			}
			break;
		default:
			return CMPACK_ERR_UNKNOWN_FORMAT;
		}
		break;

	case CMPACK_BITPIX_SLONG:
		switch (dst_format)
		{
		case CMPACK_BITPIX_SSHORT:
			{
				int32_t *sptr = (int32_t*)src;
				int16_t *dptr = (int16_t*)dst;
				for (i=0; i<pixels; i++) {
					int32_t val = *sptr++;
					if (val<INT16_MIN)
						*dptr++ = INT16_MIN;
					else if (val>INT16_MAX)
						*dptr++ = INT16_MAX;
					else
						*dptr++ = (int16_t)val;
				}
			}
			break;
		case CMPACK_BITPIX_USHORT:
			{
				int32_t *sptr = (int32_t*)src;
				uint16_t *dptr = (uint16_t*)dst;
				for (i=0; i<pixels; i++) {
					int32_t val = *sptr++;
					if (val<0)
						*dptr++ = 0;
					else if (val>UINT16_MAX)
						*dptr++ = UINT16_MAX;
					else
						*dptr++ = (uint16_t)val;
				}
			}
			break;
		case CMPACK_BITPIX_SLONG:
			memcpy(dst, src, pixels*sizeof(int32_t));
			break;
		case CMPACK_BITPIX_ULONG:
			{
				int32_t *sptr = (int32_t*)src;
				uint32_t *dptr = (uint32_t*)dst;
				for (i=0; i<pixels; i++) {
					int32_t val = *sptr++;
					if (val<=0)
						*dptr++ = 0;
					else
						*dptr++ = val;
				}
			}
			break;
		case CMPACK_BITPIX_FLOAT:
			{
				int32_t *sptr = (int32_t*)src;
				float *dptr = (float*)dst;
				for (i=0; i<pixels; i++) 
					*dptr++ = (float)(*sptr++);
			}
			break;
		case CMPACK_BITPIX_DOUBLE:
			{
				int32_t *sptr = (int32_t*)src;
				double *dptr = (double*)dst;
				for (i=0; i<pixels; i++) 
					*dptr++ = (double)(*sptr++);
			}
			break;
		default:
			return CMPACK_ERR_UNKNOWN_FORMAT;
		}
		break;

	case CMPACK_BITPIX_ULONG:
		switch (dst_format)
		{
		case CMPACK_BITPIX_SSHORT:
			{
				uint32_t *sptr = (uint32_t*)src;
				int16_t *dptr = (int16_t*)dst;
				for (i=0; i<pixels; i++) {
					uint32_t val = *sptr++;
					if (val>=INT16_MAX)
						*dptr++ = INT16_MAX;
					else
						*dptr++ = (int16_t)val;
				}
			}
			break;
		case CMPACK_BITPIX_USHORT:
			{
				uint32_t *sptr = (uint32_t*)src;
				uint16_t *dptr = (uint16_t*)dst;
				for (i=0; i<pixels; i++) {
					uint32_t val = *sptr++;
					if (val>=UINT16_MAX)
						*dptr++ = UINT16_MAX;
					else
						*dptr++ = (uint16_t)val;
				}
			}
			break;
		case CMPACK_BITPIX_SLONG:
			{
				uint32_t *sptr = (uint32_t*)src;
				int32_t *dptr = (int32_t*)dst;
				for (i=0; i<pixels; i++) {
					uint32_t val = *sptr++;
					if (val>=INT32_MAX)
						*dptr++ = INT32_MAX;
					else
						*dptr++ = val;
				}
			}
			break;
		case CMPACK_BITPIX_ULONG:
			memcpy(dst, src, pixels*sizeof(uint32_t));
			break;
		case CMPACK_BITPIX_FLOAT:
			{
				uint32_t *sptr = (uint32_t*)src;
				float *dptr = (float*)dst;
				for (i=0; i<pixels; i++) 
					*dptr++ = (float)(*sptr++);
			}
			break;
		case CMPACK_BITPIX_DOUBLE:
			{
				uint32_t *sptr = (uint32_t*)src;
				double *dptr = (double*)dst;
				for (i=0; i<pixels; i++) 
					*dptr++ = (double)(*sptr++);
			}
			break;
		default:
			return CMPACK_ERR_UNKNOWN_FORMAT;
		}
		break;

	case CMPACK_BITPIX_FLOAT:
		switch (dst_format)
		{
		case CMPACK_BITPIX_SSHORT:
			{
				float *sptr = (float*)src;
				int16_t *dptr = (int16_t*)dst;
				for (i=0; i<pixels; i++) {
					float val = *sptr++;
					if (val<=INT16_MIN)
						*dptr++ = INT16_MIN;
					else if (val>=INT16_MAX)
						*dptr++ = INT16_MAX;
					else
						*dptr++ = (int16_t)val;
				}
			}
			break;
		case CMPACK_BITPIX_USHORT:
			{
				float *sptr = (float*)src;
				uint16_t *dptr = (uint16_t*)dst;
				for (i=0; i<pixels; i++) {
					float val = *sptr++;
					if (val<=0)
						*dptr++ = 0;
					else if (val>=UINT16_MAX)
						*dptr++ = UINT16_MAX;
					else
						*dptr++ = (uint16_t)val;
				}
			}
			break;
		case CMPACK_BITPIX_SLONG:
			{
				float *sptr = (float*)src;
				int32_t *dptr = (int32_t*)dst;
				for (i=0; i<pixels; i++) {
					float val = *sptr++;
					if (val<=INT32_MIN)
						*dptr++ = INT32_MIN;
					else if (val>=INT32_MAX)
						*dptr++ = INT32_MAX;
					else
						*dptr++ = (int32_t)val;
				}
			}
			break;
		case CMPACK_BITPIX_ULONG:
			{
				float *sptr = (float*)src;
				uint32_t *dptr = (uint32_t*)dst;
				for (i=0; i<pixels; i++) {
					float val = *sptr++;
					if (val<=0)
						*dptr++ = 0;
					else if (val>=UINT32_MAX)
						*dptr++ = UINT32_MAX;
					else
						*dptr++ = (uint32_t)val;
				}
			}
			break;
		case CMPACK_BITPIX_FLOAT:
			memcpy(dst, src, pixels*sizeof(float));
			break;
		case CMPACK_BITPIX_DOUBLE:
			{
				float *sptr = (float*)src;
				double *dptr = (double*)dst;
				for (i=0; i<pixels; i++) 
					*dptr++ = (*sptr++);
			}
			break;
		default:
			return CMPACK_ERR_UNKNOWN_FORMAT;
		}
		break;

	case CMPACK_BITPIX_DOUBLE:
		switch (dst_format)
		{
		case CMPACK_BITPIX_SSHORT:
			{
				double *sptr = (double*)src;
				int16_t *dptr = (int16_t*)dst;
				for (i=0; i<pixels; i++) {
					double val = *sptr++;
					if (val<=INT16_MIN)
						*dptr++ = INT16_MIN;
					else if (val>=INT16_MAX)
						*dptr++ = INT16_MAX;
					else
						*dptr++ = (int16_t)val;
				}
			}
			break;
		case CMPACK_BITPIX_USHORT:
			{
				double *sptr = (double*)src;
				uint16_t *dptr = (uint16_t*)dst;
				for (i=0; i<pixels; i++) {
					double val = *sptr++;
					if (val<=0)
						*dptr++ = 0;
					else if (val>=UINT16_MAX)
						*dptr++ = UINT16_MAX;
					else
						*dptr++ = (uint16_t)val;
				}
			}
			break;
		case CMPACK_BITPIX_SLONG:
			{
				double *sptr = (double*)src;
				int32_t *dptr = (int32_t*)dst;
				for (i=0; i<pixels; i++) {
					double val = *sptr++;
					if (val<=INT32_MIN)
						*dptr++ = INT32_MIN;
					else if (val>=INT32_MAX)
						*dptr++ = INT32_MAX;
					else
						*dptr++ = (int32_t)val;
				}
			}
			break;
		case CMPACK_BITPIX_ULONG:
			{
				double *sptr = (double*)src;
				uint32_t *dptr = (uint32_t*)dst;
				for (i=0; i<pixels; i++) {
					double val = *sptr++;
					if (val<=0)
						*dptr++ = 0;
					else if (val>=UINT32_MAX)
						*dptr++ = UINT32_MAX;
					else
						*dptr++ = (uint32_t)val;
				}
			}
			break;
		case CMPACK_BITPIX_FLOAT:
			{
				double *sptr = (double*)src;
				float *dptr = (float*)dst;
				for (i=0; i<pixels; i++) 
					*dptr++ = (float)(*sptr++);
			}
			break;
		case CMPACK_BITPIX_DOUBLE:
			memcpy(dst, src, pixels*sizeof(double));
			break;
		default:
			return CMPACK_ERR_UNKNOWN_FORMAT;
		}
		break;

	default:
		return CMPACK_ERR_UNKNOWN_FORMAT;
	}

	return CMPACK_ERR_OK;
}

CmpackImage *cmpack_image_convert(const CmpackImage *src, CmpackBitpix format)
{
	if (src && format!=CMPACK_BITPIX_UNKNOWN) {
		if (src->format == format) {
			return cmpack_image_copy(src);
		} else {
			CmpackImage *dst = cmpack_image_new(src->width, src->height, format);
			if (dst)
				image_convert(src->data, src->width, src->height, src->format, 
					dst->data, dst->datalen, format);
			return dst;
		}
	}
	return NULL;
}

CmpackImage *cmpack_image_binning(const CmpackImage *isrc, int hbin, int vbin)
{
	int x, y, m, n, dst_width, dst_height;
	CmpackImage *idst;

	if (!isrc || hbin<=0 || vbin<=0)
		return NULL;

	dst_width = isrc->width / hbin;
	dst_height = isrc->height / vbin;
	if (dst_width<=0 || dst_height<=0)
		return NULL;

	idst = cmpack_image_new(dst_width, dst_height, isrc->format);
	if (!idst)
		return NULL;

	switch (isrc->format) 
	{
	case CMPACK_BITPIX_SSHORT:
		{
			int16_t *dptr = (int16_t*)idst->data;
			for (y=0; y<dst_height; y++) {
				for (x=0; x<dst_width; x++) {
					int val = 0;
					for (n=0; n<vbin; n++) {
						for (m=0; m<hbin; m++) 
							val += ((int16_t*)isrc->data)[(x*hbin+m)+((y*vbin+n)*isrc->width)];
					}
					if (val>=INT16_MAX)
						*dptr++ = INT16_MAX;
					else if (val<=INT16_MIN)
						*dptr++ = INT16_MIN;
					else
						*dptr++ = (int16_t)val;
				}
			}
		}
		break;

	case CMPACK_BITPIX_USHORT:
		{
			uint16_t *dptr = (uint16_t*)idst->data;
			for (y=0; y<dst_height; y++) {
				for (x=0; x<dst_width; x++) {
					int val = 0;
					for (n=0; n<vbin; n++) {
						for (m=0; m<hbin; m++) 
							val += ((uint16_t*)isrc->data)[(x*hbin+m)+((y*vbin+n)*isrc->width)];
					}
					if (val>=UINT16_MAX)
						*dptr++ = UINT16_MAX;
					else if (val<=0)
						*dptr++ = 0;
					else
						*dptr++ = (uint16_t)val;
				}
			}
		}
		break;

	case CMPACK_BITPIX_SLONG:
		{
			int32_t *dptr = (int32_t*)idst->data;
			for (y=0; y<dst_height; y++) {
				for (x=0; x<dst_width; x++) {
					int32_t val = 0;
					for (n=0; n<vbin; n++) {
						for (m=0; m<hbin; m++) 
							val += ((int32_t*)isrc->data)[(x*hbin+m)+((y*vbin+n)*isrc->width)];
					}
					*dptr++ = val;
				}
			}
		}
		break;

	case CMPACK_BITPIX_ULONG:
		{
			uint32_t *dptr = (uint32_t*)idst->data;
			for (y=0; y<dst_height; y++) {
				for (x=0; x<dst_width; x++) {
					uint32_t val = 0;
					for (n=0; n<vbin; n++) {
						for (m=0; m<hbin; m++) 
							val += ((uint32_t*)isrc->data)[(x*hbin+m)+((y*vbin+n)*isrc->width)];
					}
					*dptr++ = val;
				}
			}
		}
		break;

	case CMPACK_BITPIX_FLOAT:
		{
			float *dptr = (float*)idst->data;
			for (y=0; y<dst_height; y++) {
				for (x=0; x<dst_width; x++) {
					float val = 0;
					for (n=0; n<vbin; n++) {
						for (m=0; m<hbin; m++) 
							val += ((float*)isrc->data)[(x*hbin+m)+((y*vbin+n)*isrc->width)];
					}
					*dptr++ = val;
				}
			}
		}
		break;

	case CMPACK_BITPIX_DOUBLE:
		{
			double *dptr = (double*)idst->data;
			for (y=0; y<dst_height; y++) {
				for (x=0; x<dst_width; x++) {
					double val = 0;
					for (n=0; n<vbin; n++) {
						for (m=0; m<hbin; m++) 
							val += ((double*)isrc->data)[(x*hbin+m)+((y*vbin+n)*isrc->width)];
					}
					*dptr++ = val;
				}
			}
		}
		break;

	default:
		cmpack_image_destroy(idst);
		return NULL;
	}

	return idst;
}

int cmpack_image_transpose(CmpackImage *iimg, int hflip, int vflip)
{
	int x, y, width, height;
	void *img;

	width = iimg->width;
	height = iimg->height;
	img = iimg->data;

	switch (iimg->format) 
	{
	case CMPACK_BITPIX_SSHORT:
		if (hflip) {
			for (y=0; y<height; y++) {
				for (x=0; x<width/2; x++) {
					int16_t *p1 = (int16_t*)img+(x+y*width), 
						*p2 = (int16_t*)img+(width-1-x+y*width);
					int16_t aux = *p1;
					*p1 = *p2;
					*p2 = aux;
				}
			}
		}
		if (vflip) {
			for (x=0; x<width; x++) {
				for (y=0; y<height/2; y++) {
					int16_t *p1 = (int16_t*)img+(x+y*width), 
						*p2 = (int16_t*)img+(x+(height-1-y)*width);
					int16_t aux = *p1;
					*p1 = *p2;
					*p2 = aux;
				}
			}
		}
		break;

	case CMPACK_BITPIX_USHORT:
		if (hflip) {
			for (y=0; y<height; y++) {
				for (x=0; x<width/2; x++) {
					uint16_t *p1 = (uint16_t*)img+(x+y*width), 
						*p2 = (uint16_t*)img+(width-1-x+y*width);
					uint16_t aux = *p1;
					*p1 = *p2;
					*p2 = aux;
				}
			}
		}
		if (vflip) {
			for (x=0; x<width; x++) {
				for (y=0; y<height/2; y++) {
					uint16_t *p1 = (uint16_t*)img+(x+y*width), 
						*p2 = (uint16_t*)img+(x+(height-1-y)*width);
					uint16_t aux = *p1;
					*p1 = *p2;
					*p2 = aux;
				}
			}
		}
		break;

	case CMPACK_BITPIX_SLONG:
		if (hflip) {
			for (y=0; y<height; y++) {
				for (x=0; x<width/2; x++) {
					int32_t *p1 = (int32_t*)img+(x+y*width), 
						*p2 = (int32_t*)img+(width-1-x+y*width);
					int32_t aux = *p1;
					*p1 = *p2;
					*p2 = aux;
				}
			}
		}
		if (vflip) {
			for (x=0; x<width; x++) {
				for (y=0; y<height/2; y++) {
					int32_t *p1 = (int32_t*)img+(x+y*width), 
						*p2 = (int32_t*)img+(x+(height-1-y)*width);
					int32_t aux = *p1;
					*p1 = *p2;
					*p2 = aux;
				}
			}
		}
		break;

	case CMPACK_BITPIX_ULONG:
		if (hflip) {
			for (y=0; y<height; y++) {
				for (x=0; x<width/2; x++) {
					uint32_t *p1 = (uint32_t*)img+(x+y*width), 
						*p2 = (uint32_t*)img+(width-1-x+y*width);
					uint32_t aux = *p1;
					*p1 = *p2;
					*p2 = aux;
				}
			}
		}
		if (vflip) {
			for (x=0; x<width; x++) {
				for (y=0; y<height/2; y++) {
					uint32_t *p1 = (uint32_t*)img+(x+y*width), 
						*p2 = (uint32_t*)img+(x+(height-1-y)*width);
					uint32_t aux = *p1;
					*p1 = *p2;
					*p2 = aux;
				}
			}
		}
		break;

	case CMPACK_BITPIX_FLOAT:
		if (hflip) {
			for (y=0; y<height; y++) {
				for (x=0; x<width/2; x++) {
					float *p1 = (float*)img+(x+y*width), 
						*p2 = (float*)img+(width-1-x+y*width);
					float aux = *p1;
					*p1 = *p2;
					*p2 = aux;
				}
			}
		}
		if (vflip) {
			for (x=0; x<width; x++) {
				for (y=0; y<height/2; y++) {
					float *p1 = (float*)img+(x+y*width), 
						*p2 = (float*)img+(x+(height-1-y)*width);
					float aux = *p1;
					*p1 = *p2;
					*p2 = aux;
				}
			}
		}
		break;

	case CMPACK_BITPIX_DOUBLE:
		if (hflip) {
			for (y=0; y<height; y++) {
				for (x=0; x<width/2; x++) {
					double *p1 = (double*)img+(x+y*width), 
						*p2 = (double*)img+(width-1-x+y*width);
					double aux = *p1;
					*p1 = *p2;
					*p2 = aux;
				}
			}
		}
		if (vflip) {
			for (x=0; x<width; x++) {
				for (y=0; y<height/2; y++) {
					double *p1 = (double*)img+(x+y*width), 
						*p2 = (double*)img+(x+(height-1-y)*width);
					double aux = *p1;
					*p1 = *p2;
					*p2 = aux;
				}
			}
		}
		break;
		
	default:
		return CMPACK_ERR_UNKNOWN_FORMAT;
	}

	return CMPACK_ERR_OK;
}

int cmpack_image_fillrect(CmpackImage *iimg, int left, int top, int width, int height,
	double value)
{
	int x, y, nx, ny;
	void *img;

	nx = iimg->width;
	ny = iimg->height;
	if (left<0 || top<0 || width<0 || height<0 || left+width>nx || top+height>ny)
		return CMPACK_ERR_OUT_OF_RANGE;
	
	img = iimg->data;
	switch (iimg->format) 
	{
	case CMPACK_BITPIX_SSHORT:
		{
			int16_t val = (int16_t)value;
			for (y=0; y<height; y++) {
				int16_t *dptr = ((int16_t*)img) + (left+(top+y)*nx);
				for (x=0; x<width; x++)
					 *dptr++ = val;
			}
		}
		break;

	case CMPACK_BITPIX_USHORT:
		{
			uint16_t val = (uint16_t)value;
			for (y=0; y<height; y++) {
				uint16_t *dptr = ((uint16_t*)img) + (left+(top+y)*nx);
				for (x=0; x<width; x++)
					 *dptr++ = val;
			}
		}
		break;

	case CMPACK_BITPIX_SLONG:
		{
			int32_t val = (int32_t)value;
			for (y=0; y<height; y++) {
				int32_t *dptr = ((int32_t*)img) + ((left)+(top+y)*nx);
				for (x=0; x<width; x++)
					 *dptr++ = val;
			}
		}
		break;

	case CMPACK_BITPIX_ULONG:
		{
			uint32_t val = (uint32_t)value;
			for (y=0; y<height; y++) {
				uint32_t *dptr = ((uint32_t*)img) + (left+(top+y)*nx);
				for (x=0; x<width; x++)
					 *dptr++ = val;
			}
		}
		break;

	case CMPACK_BITPIX_FLOAT:
		{
			float val = (float)value;
			for (y=0; y<height; y++) {
				float *dptr = ((float*)img) + (left+(top+y)*nx);
				for (x=0; x<width; x++)
						*dptr++ = val;
			}
		}
		break;
		
	case CMPACK_BITPIX_DOUBLE:
		{
			for (y=0; y<height; y++) {
				double *dptr = ((double*)img) + (left+(top+y)*nx);
				for (x=0; x<width; x++)
						*dptr++ = value;
			}
		}
		break;

	default:
		return CMPACK_ERR_UNKNOWN_FORMAT;
	}

	return CMPACK_ERR_OK;
}

/* Get single pixel value */
double cmpack_image_getpixel(const CmpackImage *img, int x, int y)
{
	if (img && x>=0 && y>=0 && x<img->width && y<img->height) {
		switch (img->format)
		{
		case CMPACK_BITPIX_SSHORT:
			return ((int16_t*)img->data)[y*img->width+x];
		case CMPACK_BITPIX_USHORT:
			return ((uint16_t*)img->data)[y*img->width+x];
		case CMPACK_BITPIX_SLONG:
			return ((int32_t*)img->data)[y*img->width+x];
		case CMPACK_BITPIX_ULONG:
			return ((uint32_t*)img->data)[y*img->width+x];
		case CMPACK_BITPIX_FLOAT:
			return ((float*)img->data)[y*img->width+x];
		case CMPACK_BITPIX_DOUBLE:
			return ((double*)img->data)[y*img->width+x];
		default:
			break;
		}
	}
	return 0.0;
}

/* Get minimum and maximum pixel value */
static int cmpack_image_minmax_SSHORT(CmpackImage *img, int nulvalue, double *pmin, double *pmax)
{
	int ok = 0, i, pixels = img->width*img->height;
	int min = 0, max = 0;
	int16_t *sptr = (int16_t*)img->data;
	
	for (i=0; i<pixels; i++) {
		int val = (*sptr++);
		if (val>nulvalue) {
			if (!ok) {
				min = max = val;
				ok = 1;
			} else {
				if (val < min)
					min = val;
				if (val > max)
					max = val;
			}
		}
	}
	if (pmin)
		*pmin = min;
	if (pmax)
		*pmax = max;
	return (ok ? CMPACK_ERR_OK : CMPACK_ERR_UNDEF_VALUE);
}

/* Get minimum and maximum pixel value */
static int cmpack_image_minmax_USHORT(CmpackImage *img, int nulvalue, double *pmin, double *pmax)
{
	int ok = 0, i, pixels = img->width*img->height;
	int min = 0, max = 0;
	uint16_t *sptr = (uint16_t*)img->data;

	for (i=0; i<pixels; i++) {
		int val = *sptr++;
		if (val>nulvalue) {
			if (!ok) {
				min = max = val;
				ok = 1;
			} else {
				if (val < min)
					min = val;
				if (val > max)
					max = val;
			}
		}
	}
	if (pmin)
		*pmin = min;
	if (pmax)
		*pmax = max;
	return (ok ? CMPACK_ERR_OK : CMPACK_ERR_UNDEF_VALUE);
}

/* Get minimum and maximum pixel value */
static int cmpack_image_minmax_SLONG(CmpackImage *img, int nulvalue, double *pmin, double *pmax)
{
	int ok = 0, i, pixels = img->width*img->height;
	int min = 0, max = 0;
	int32_t *sptr = (int32_t*)img->data;

	for (i=0; i<pixels; i++) {
		int val = *sptr++;
		if (val>nulvalue) {
			if (!ok) {
				min = max = val;
				ok = 1;
			} else {
				if (min > val)
					min = val;
				if (max < val)
					max = val;
			}
		}
	}
	if (pmin)
		*pmin = min;
	if (pmax)
		*pmax = max;
	return (ok ? CMPACK_ERR_OK : CMPACK_ERR_UNDEF_VALUE);
}

/* Get minimum and maximum pixel value */
static int cmpack_image_minmax_ULONG(CmpackImage *img, unsigned nulvalue, double *pmin, double *pmax)
{
	int i, ok = 0, pixels = img->width*img->height;
	unsigned min = 0, max = 0;
	uint32_t *sptr = (uint32_t*)img->data;

	for (i=0; i<pixels; i++) {
		unsigned val = *sptr++;
		if (val>nulvalue) {
			if (!ok) {
				min = max = val;
				ok = 1;
			} else {
				if (min > val)
					min = val;
				if (max < val)
					max = val;
			}
		}
	}
	if (pmin)
		*pmin = min;
	if (pmax)
		*pmax = max;
	return (ok ? CMPACK_ERR_OK : CMPACK_ERR_UNDEF_VALUE);
}

/* Get minimum and maximum pixel value */
static int cmpack_image_minmax_FLOAT(CmpackImage *img, double nulvalue, double *pmin, double *pmax)
{
	int ok = 0, i, pixels = img->width*img->height;
	double min = 0, max = 0;
	float *sptr = (float*)img->data;

	for (i=0; i<pixels; i++) {
		float val = *sptr++;
		if (val>nulvalue) {
			if (!ok) {
				min = max = val;
				ok = 1;
			} else {
				if (min > val)
					min = val;
				if (max < val)
					max = val;
			}
		}
	}
	if (pmin)
		*pmin = min;
	if (pmax)
		*pmax = max;
	return (ok ? CMPACK_ERR_OK : CMPACK_ERR_UNDEF_VALUE);
}

/* Get minimum and maximum pixel value */
static int cmpack_image_minmax_DOUBLE(CmpackImage *img, double nulvalue, double *pmin, double *pmax)
{
	int		ok = 0, i, pixels = img->width*img->height;
	double	min = 0, max = 0, *sptr = (double*)img->data;

	for (i=0; i<pixels; i++) {
		double val = *sptr++;
		if (val>nulvalue) {
			if (!ok) {
				min = max = val;
				ok = 1;
			} else {
				if (min > val)
					min = val;
				if (max < val)
					max = val;
			}
		}
	}
	if (pmin)
		*pmin = min;
	if (pmax)
		*pmax = max;
	return (ok ? CMPACK_ERR_OK : CMPACK_ERR_UNDEF_VALUE);
}

/* Determine pixel value range */
int cmpack_image_minmax(CmpackImage *img, double nulvalue, double badvalue, double *minvalue, 
	double *maxvalue)
{
	*minvalue = *maxvalue = 0;

	if (img->width==0 || img->height==0)  
		return CMPACK_ERR_UNDEF_VALUE;

	switch (img->format)
	{
	case CMPACK_BITPIX_SSHORT:
		return cmpack_image_minmax_SSHORT(img, (int)nulvalue, minvalue, maxvalue); 
	case CMPACK_BITPIX_USHORT:
		return cmpack_image_minmax_USHORT(img, (int)nulvalue, minvalue, maxvalue);
	case CMPACK_BITPIX_SLONG:
		return cmpack_image_minmax_SLONG(img, (int)nulvalue, minvalue, maxvalue);
	case CMPACK_BITPIX_ULONG:
		return cmpack_image_minmax_ULONG(img, (unsigned)nulvalue, minvalue, maxvalue);  
	case CMPACK_BITPIX_FLOAT:
		return cmpack_image_minmax_FLOAT(img, nulvalue, minvalue, maxvalue);  
	case CMPACK_BITPIX_DOUBLE:
		return cmpack_image_minmax_DOUBLE(img, nulvalue, minvalue, maxvalue); 
	default:
		return CMPACK_ERR_INVALID_BITPIX;
	}
}

/* Get minimum and maximum pixel value */
static int cmpack_image_histogram_SSHORT(CmpackImage *img, int length, double ch_width, 
	double offset, unsigned *hist)
{
	int		i, pixels = img->width*img->height;
	int16_t	*sptr = (int16_t*)img->data;

	for (i=0; i<pixels; i++) {
		double d = ((*sptr++)-offset)/ch_width;
		if (d>=0 && d<length)
			hist[(int)d]++;
	}
	return CMPACK_ERR_OK;
}

/* Get minimum and maximum pixel value */
static int cmpack_image_histogram_USHORT(CmpackImage *img, int length, double ch_width, 
	double offset, unsigned *hist)
{
	int		i, pixels = img->width*img->height;
	uint16_t *sptr = (uint16_t*)img->data;

	for (i=0; i<pixels; i++) {
		double d = ((*sptr++)-offset)/ch_width;
		if (d>=0 && d<length)
			hist[(int)d]++;
	}
	return CMPACK_ERR_OK;
}

/* Get minimum and maximum pixel value */
static int cmpack_image_histogram_SLONG(CmpackImage *img, int length, double ch_width, 
	double offset, unsigned *hist)
{
	int		i, pixels = img->width*img->height;
	int32_t *sptr = (int32_t*)img->data;

	for (i=0; i<pixels; i++) {
		double d = ((*sptr++)-offset)/ch_width;
		if (d>=0 && d<length)
			hist[(int)d]++;
	}
	return CMPACK_ERR_OK;
}

/* Get minimum and maximum pixel value */
static int cmpack_image_histogram_ULONG(CmpackImage *img, int length, double ch_width, 
	double offset, unsigned *hist)
{
	int		i, pixels = img->width*img->height;
	uint32_t *sptr = (uint32_t*)img->data;

	for (i=0; i<pixels; i++) {
		double d = ((*sptr++)-offset)/ch_width;
		if (d>=0 && d<length)
			hist[(int)d]++;
	}
	return 0;
}

/* Get minimum and maximum pixel value */
static int cmpack_image_histogram_FLOAT(CmpackImage *img, int length, double ch_width, 
	double offset, unsigned *hist)
{
	int		i, pixels = img->width*img->height;
	float	*sptr = (float*)img->data;

	for (i=0; i<pixels; i++) {
		double d = ((*sptr++)-offset)/ch_width;
		if (d>=0 && d<length)
			hist[(int)d]++;
	}
	return 0;
}

/* Get minimum and maximum pixel value */
static int cmpack_image_histogram_DOUBLE(CmpackImage *img, int length, double ch_width, 
	double offset, unsigned *hist)
{
	int		i, pixels = img->width*img->height;
	double	*sptr = (double*)img->data;

	for (i=0; i<pixels; i++) {
		double d = ((*sptr++)-offset)/ch_width;
		if (d>=0 && d<length)
			hist[(int)d]++;
	}
	return 0;
}

/* Make histogram */
int cmpack_image_histogram(CmpackImage *img, int length, double ch_width, double offset, unsigned *hist)
{
	if (img->width==0 || img->height==0)  
		return CMPACK_ERR_UNDEF_VALUE;
	if (ch_width<=0 || length<=0)
		return CMPACK_ERR_INVALID_PAR;

	memset(hist, 0, length*sizeof(unsigned));
	switch (img->format)
	{
	case CMPACK_BITPIX_SSHORT:
		return cmpack_image_histogram_SSHORT(img, length, ch_width, offset, hist); 
	case CMPACK_BITPIX_USHORT:
		return cmpack_image_histogram_USHORT(img, length, ch_width, offset, hist); 
	case CMPACK_BITPIX_SLONG:
		return cmpack_image_histogram_SLONG(img, length, ch_width, offset, hist); 
	case CMPACK_BITPIX_ULONG:
		return cmpack_image_histogram_ULONG(img, length, ch_width, offset, hist); 
	case CMPACK_BITPIX_FLOAT:
		return cmpack_image_histogram_FLOAT(img, length, ch_width, offset, hist); 
	case CMPACK_BITPIX_DOUBLE:
		return cmpack_image_histogram_DOUBLE(img, length, ch_width, offset, hist); 
	default:
		return CMPACK_ERR_INVALID_BITPIX;
	}
}

/* Get minimum and maximum pixel value */
static int cmpack_image_autogb_SSHORT(CmpackImage *img, int nulvalue, int badvalue, double *black, double *white)
{
	int			pixels = img->width*img->height, minvalue=nulvalue+1, maxvalue=badvalue-1;
	int			i, j, k, count, hsize = maxvalue-minvalue+1;
	uint32_t	*hist, valid;
	int16_t		*sptr = (int16_t*)img->data;

	hist = (uint32_t*)cmpack_calloc(hsize, sizeof(uint32_t));
	for (i=0, valid=0; i<pixels; i++) {
		int val = (*sptr++);
		if (val>=minvalue && val<=maxvalue) {
			valid++;
			hist[val-minvalue]++;
		}
	}
	for (j=0, count=0; j<hsize-1 && count<0.01*valid; j++)
		count += hist[j];
	*black = j+minvalue;
	for (k=hsize-1, count=0; k>j && count<0.01*valid; k--)
		count += hist[k];
	*white = k+minvalue;
	cmpack_free(hist);
	return CMPACK_ERR_OK;
}

/* Get minimum and maximum pixel value */
static int cmpack_image_autogb_USHORT(CmpackImage *img, int nulvalue, int badvalue, double *black, double *white)
{
	int			pixels = img->width*img->height, minvalue=nulvalue+1, maxvalue=badvalue-1;
	int			i, j, k, count, hsize = maxvalue-minvalue+1;
	uint32_t	*hist, valid;
	uint16_t	*sptr = (uint16_t*)img->data;

	hist = (uint32_t*)cmpack_calloc(hsize, sizeof(uint32_t));
	for (i=0, valid=0; i<pixels; i++) {
		int val = *sptr++;
		if (val>=minvalue && val<=maxvalue) {
			valid++;
			hist[val-minvalue]++;
		}
	}
	for (j=0, count=0; j<hsize-1 && count<0.01*pixels; j++)
		count += hist[j];
	*black = j+minvalue;
	for (k=hsize-1, count=0; k>j && count<0.01*pixels; k--)
		count += hist[k];
	*white = k+minvalue;
	cmpack_free(hist);
	return CMPACK_ERR_OK;
}

/* Number of pixels than are less than specified value */
static unsigned cmpack_image_npixels_SLONG(CmpackImage *img, int minvalue, int maxvalue)
{
	int		i, pixels = img->width*img->height;
	int32_t *sptr = (int32_t*)img->data;

	unsigned count = 0;
	for (i=0; i<pixels; i++) {
		int val = *sptr++;
		if (val>=minvalue && val<=maxvalue)
			count++;
	}
	return count;
}

/* Get minimum and maximum pixel value */
static int cmpack_image_autogb_SLONG(CmpackImage *img, int nulvalue, int badvalue, double *black, double *white)
{
	unsigned	i, pixels = img->width*img->height, valid;
	int32_t		min, max, start, end, *sptr;
	
	sptr = (int32_t*)img->data;
	for (i=0, valid=0, min=0, max=0; i<pixels; i++) {
		int val = *sptr++;
		if (val>nulvalue && val<badvalue) {
			if (valid==0) {
				min = max = val;
			} else {
				if (min > val)
					min = val;
				if (max < val)
					max = val;
			}
			valid++;
		}
	}

	start = min, end = max;
	while (end-start>1) {
		int32_t mid = (int32_t)(((double)start+end)/2);
		unsigned count = cmpack_image_npixels_SLONG(img, min, mid-1);
		if (count>0.01*valid)
			end = mid;
		else if (count<0.01*valid)
			start = mid;
		else
			start = end = mid;
	}
	*black = start;
	start = min, end = max;
	while (end-start>1) {
		int32_t mid = (int32_t)(((double)start+end)/2);
		unsigned count = cmpack_image_npixels_SLONG(img, mid+1, max);
		if (count>0.01*valid)
			start = mid;
		else if (count<0.01*valid)
			end = mid;
		else
			start = end = mid;
	}
	*white = end;

	return CMPACK_ERR_OK;
}

/* Number of pixels than are less than specified value */
static unsigned cmpack_image_npixels_ULONG(CmpackImage *img, unsigned minvalue, unsigned maxvalue)
{
	int		i, pixels = img->width*img->height;
	uint32_t *sptr = (uint32_t*)img->data;

	unsigned count = 0;
	for (i=0; i<pixels; i++) {
		unsigned val = *sptr++;
		if (val>=minvalue && val<=maxvalue)
			count++;
	}
	return count;
}

/* Get minimum and maximum pixel value */
static int cmpack_image_autogb_ULONG(CmpackImage *img, unsigned nulvalue, unsigned badvalue, double *black, double *white)
{
	unsigned	i, pixels = img->width*img->height,valid;
	uint32_t	min, max, start, end, *sptr;

	sptr = (uint32_t*)img->data;
	for (i=0, valid=0, min=0, max=0; i<pixels; i++) {
		uint32_t val = *sptr++;
		if (val>nulvalue && val<badvalue) {
			if (valid==0) {
				min = max = val;
			} else {
				if (min > val)
					min = val;
				if (max < val)
					max = val;
			}
			valid++;
		}
	}
	
	start = min, end = max;
	while (end-start>1) {
		uint32_t mid = (uint32_t)(((double)start+end)/2);
		unsigned count = cmpack_image_npixels_ULONG(img, min, mid-1);
		if (count>0.01*valid)
			end = mid;
		else if (count<0.01*valid)
			start = mid;
		else
			start = end = mid;
	}
	*black = start;
	start = min, end = max;
	while (end-start>1) {
		uint32_t mid = (uint32_t)(((double)start+end)/2);
		unsigned count = cmpack_image_npixels_ULONG(img, mid+1, max);
		if (count>0.01*valid)
			start = mid;
		else if (count<0.01*valid)
			end = mid;
		else
			start = end = mid;
	}
	*white = end;

	return CMPACK_ERR_OK;
}

/* Number of pixels than are less than specified value */
static unsigned cmpack_image_npixels_FLOAT(CmpackImage *img, double nulvalue, double badvalue)
{
	int		i, pixels = img->width*img->height;
	float   *sptr = (float*)img->data;

	unsigned count = 0;
	for (i=0; i<pixels; i++) {
		double val = *sptr++;
		if (val>nulvalue && val<badvalue)
			count++;
	}
	return count;
}

/* Get minimum and maximum pixel value */
static int cmpack_image_autogb_FLOAT(CmpackImage *img, double nulvalue, double badvalue, double *black, double *white)
{
	unsigned	i, pixels = img->width*img->height, valid;
	float		min, max, start, end, *sptr;

	sptr = (float*)img->data;
	for (i=0, valid=0, min=0, max=0; i<pixels; i++) {
		float val = *sptr++;
		if (val>nulvalue && val<badvalue) {
			if (valid==0) {
				min = max = val;
			} else {
				if (min > val)
					min = val;
				if (max < val)
					max = val;
			}
			valid++;
		}
	}

	start = min, end = max;
	while (end-start>1) {
		float mid = ((start+end)/2);
		unsigned count = cmpack_image_npixels_FLOAT(img, nulvalue, mid);
		if (count>0.01*valid)
			end = mid;
		else if (count<0.01*valid)
			start = mid;
		else
			start = end = mid;
	}
	*black = start;
	start = min, end = max;
	while (end-start>1) {
		float mid = ((start+end)/2);
		unsigned count = cmpack_image_npixels_FLOAT(img, mid, badvalue);
		if (count>0.01*valid)
			start = mid;
		else if (count<0.01*valid)
			end = mid;
		else
			start = end = mid;
	}
	*white = end;

	return CMPACK_ERR_OK;
}

/* Number of pixels than are less than specified value */
static unsigned cmpack_image_npixels_DOUBLE(CmpackImage *img, double nulvalue, double badvalue)
{
	int		i, pixels = img->width*img->height;
	double  *sptr = (double*)img->data;

	unsigned count = 0;
	for (i=0; i<pixels; i++) {
		double val = *sptr++;
		if (val>nulvalue && val<badvalue)
			count++;
	}
	return count;
}

/* Get minimum and maximum pixel value */
static int cmpack_image_autogb_DOUBLE(CmpackImage *img, double nulvalue, double badvalue, double *black, double *white)
{
	unsigned	i, pixels = img->width*img->height, valid;
	double		min, max, start, end, *sptr;

	sptr = (double*)img->data;
	for (i=0, valid=0, min=0, max=0; i<pixels; i++) {
		double val = *sptr++;
		if (val>nulvalue && val<badvalue) {
			if (valid==0) {
				min = max = val;
			} else {
				if (min > val)
					min = val;
				if (max < val)
					max = val;
			}
			valid++;
		}
	}

	start = min, end = max;
	while (end-start>1) {
		double mid = ((start+end)/2);
		unsigned count = cmpack_image_npixels_DOUBLE(img, nulvalue, mid);
		if (count>0.01*valid)
			end = mid;
		else if (count<0.01*valid)
			start = mid;
		else
			start = end = mid;
	}
	*black = start;
	start = min, end = max;
	while (end-start>1) {
		double mid = ((start+end)/2);
		unsigned count = cmpack_image_npixels_DOUBLE(img, mid, badvalue);
		if (count>0.01*valid)
			start = mid;
		else if (count<0.01*valid)
			end = mid;
		else
			start = end = mid;
	}
	*white = end;

	return CMPACK_ERR_OK;
}

int cmpack_image_autogb(CmpackImage *img, double nulvalue, double badvalue, double *black, double *white)
{
	*black = *white = 0.0;

	if (img->width==0 || img->height==0)  
		return CMPACK_ERR_UNDEF_VALUE;

	switch (img->format)
	{
	case CMPACK_BITPIX_SSHORT:
		return cmpack_image_autogb_SSHORT(img, (int)nulvalue, (int)badvalue, black, white);
	case CMPACK_BITPIX_USHORT:
		return cmpack_image_autogb_USHORT(img, (int)nulvalue, (int)badvalue, black, white);
	case CMPACK_BITPIX_SLONG:
		return cmpack_image_autogb_SLONG(img, (int)nulvalue, (int)badvalue, black, white);
	case CMPACK_BITPIX_ULONG:
		return cmpack_image_autogb_ULONG(img, (unsigned)nulvalue, (unsigned)badvalue, black, white);
	case CMPACK_BITPIX_FLOAT:
		return cmpack_image_autogb_FLOAT(img, nulvalue, badvalue, black, white);
	case CMPACK_BITPIX_DOUBLE:
		return cmpack_image_autogb_DOUBLE(img, nulvalue, badvalue, black, white);
	default:
		return CMPACK_ERR_INVALID_BITPIX;
	}
}

int cmpack_image_meandev(CmpackImage *img, double nulvalue, double badvalue, double *mean, 
	double *stddev)
{
	static const int nmax = 10000;

	int i, j, res, nstep, pixels;
    double value, *fbuf;

	*mean = *stddev = 0.0;

	if (img->width==0 || img->height==0)  
		return CMPACK_ERR_UNDEF_VALUE;
    
	pixels = img->width*img->height;
	nstep = (int)(sqrt(1.0*pixels/nmax)+1);
    fbuf = (double*)cmpack_calloc((pixels/nstep+1), sizeof(double));
    for (i=j=0; i<pixels; i+=nstep) {
		switch (img->format)
		{
		case CMPACK_BITPIX_SSHORT:
			value = ((int16_t*)img->data)[i];
			break;
		case CMPACK_BITPIX_USHORT:
			value = ((uint16_t*)img->data)[i];
			break;
		case CMPACK_BITPIX_SLONG:
			value = ((int32_t*)img->data)[i];
			break;
		case CMPACK_BITPIX_ULONG:
			value = ((uint32_t*)img->data)[i];
			break;
		case CMPACK_BITPIX_FLOAT:
			value = ((float*)img->data)[i];
			break;
		case CMPACK_BITPIX_DOUBLE:
			value = ((double*)img->data)[i];
			break;
		default:
			value = nulvalue;
		}
		if (value > nulvalue)
	        fbuf[j++] = value;
    }
	res = cmpack_robustmean(j, fbuf, mean, stddev);
	cmpack_free(fbuf);
	return (res==0 ? CMPACK_ERR_OK : CMPACK_ERR_UNDEF_VALUE);
}

CmpackImage *cmpack_image_matrix_transform(const CmpackImage *isrc, double nulvalue,
	double badvalue, const CmpackBorder *border, const CmpackMatrix *m)
{
	int		i, j, ii, jj, width, height;
	int		minx, miny, maxx, maxy;
	double	*src, *dst, sx, sy, x00, x01, x10, x11, fx, fy;
	CmpackImage *dsrc, *ddst;

	width = isrc->width;
	height = isrc->height;
	if (border) {
		minx = border->left;
		maxx = width - border->right-1;
		miny = border->top;
		maxy = height - border->bottom-1;
	} else {
		minx = miny = 0;
		maxx = width - 1;
		maxy = height - 1;
	}

	if (isrc->format == CMPACK_BITPIX_DOUBLE) {
		dsrc = NULL;
		src = (double*)isrc->data;
	} else {
		dsrc = cmpack_image_convert(isrc, CMPACK_BITPIX_DOUBLE);
		if (!dsrc) 
			return NULL;
		src = (double*)dsrc->data;
	}

	ddst = cmpack_image_new(width, height, CMPACK_BITPIX_DOUBLE);
	if (!ddst) {
		cmpack_image_destroy(dsrc);
		return NULL;
	}
	dst = (double*)ddst->data;

	for (j=0;j<height;j++) {
	    for (i=0;i<width;i++) {
			sx = i*m->xx + j*m->xy + m->x0;
			sy = i*m->yx + j*m->yy + m->y0;
		    if (sx<minx || sy<miny || sx>maxx || sy>maxy) {
		      	D(i,j) = nulvalue;
	      	} else {
				ii = (int)floor(sx);
				jj = (int)floor(sy);
				if (ii==maxx && jj==maxy) {
					x00 = x01 = x10 = x11 = S(ii, jj);
				} else if (ii==maxx) {
					x10 = x00 = S(ii, jj);
					x01 = x11 = S(ii, jj+1);
				} else if (jj==maxy) {
					x01 = x00 = S(ii, jj);
					x10 = x11 = S(ii+1, jj);
				} else {
					x00 = S(ii, jj);	x01 = S(ii, jj+1);
					x10 = S(ii+1, jj);	x11 = S(ii+1, jj+1);
				}
				if (x00>=badvalue || x10>=badvalue || x01>=badvalue || x11>=badvalue) {
	              	D(i,j) = badvalue;
	           	} else 
	           	if (x00<=nulvalue || x10<=nulvalue || x01<=nulvalue || x11<=nulvalue) {
		            D(i,j) = nulvalue;
	            } else {
					fx = sx - ii;
					fy = sy - jj;
		            D(i,j) = x00*(1-fx)*(1-fy) + x10*fx*(1-fy) + x01*(1-fx)*fy + x11*fx*fy;
	            }
	  	    }
	    }
    }

	if (dsrc)
		cmpack_image_destroy(dsrc);

	if (isrc->format == CMPACK_BITPIX_DOUBLE) {
		return ddst;
	} else {
		CmpackImage *idst = cmpack_image_convert(ddst, isrc->format);
		cmpack_image_destroy(ddst);
		return idst;
	}
}
