/* im_msb
 *
 * Copyright: 2006, The Nottingham Trent University
 *
 * Author: Tom Vajzovic
 *
 * Written on: 2006-03-13
 * 27/9/06
 * 	- removed extra im_free() in im_copy() fallback
 * 4/10/06
 * 	- removed warning on uchar fallback: it happens a lot with nip2 and
 * 	  isn't very serious
 * 1/2/10
 * 	- revised, cleanups
 * 	- gtkdoc
 */

/*

    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

 */

/** HEADERS **/

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

#include <vips/vips.h>

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

typedef struct _Msb {
	size_t index;
	size_t width;
	size_t repeat;
} Msb;

static void
byte_select( unsigned char *in, unsigned char *out, int n, Msb *msb )
{
	unsigned char *stop = out + n * msb->repeat;

	for( in += msb->index; out < stop; in += msb->width, ++out )
		*out = *in;
}

static void
byte_select_flip( unsigned char *in, unsigned char *out, int n, Msb *msb )
{
	unsigned char *stop = out + n * msb->repeat;

	for( in += msb->index; out < stop; in += msb->width, ++out )
		*out = 0x80 ^ *in;
}

static void
msb_labq( unsigned char *in, unsigned char *out, int n )
{
	unsigned char *stop = in + (n << 2);

	for( ; in < stop; in += 4, out += 3 ) {
		out[0] = in[0];
		out[1] = 0x80 ^ in[1];
		out[2] = 0x80 ^ in[2];
	}
}

/**
 * im_msb:
 * @in: input image
 * @out: output image
 *
 * Turn any integer image to 8-bit unsigned char by discarding all but the most
 * significant byte.
 * Signed values are converted to unsigned by adding 128.
 *
 * This operator also works for LABQ coding.
 *
 * See also: im_msb_band().
 *
 * Returns: 0 on success, -1 on error
 */
int
im_msb( IMAGE *in, IMAGE *out )
{
	Msb *msb;
	im_wrapone_fn func;

	if( in->Coding == IM_CODING_NONE &&
		in->BandFmt == IM_BANDFMT_UCHAR )
		return( im_copy( in, out ) );

	if( im_piocheck( in, out ) ||
		!(msb = IM_NEW( out, Msb )) )
		return( -1 );

	if( in->Coding == IM_CODING_NONE ) {
		if( im_check_int( "im_msb", in ) ) 
			return( -1 );

		msb->width = IM_IMAGE_SIZEOF_ELEMENT( in );
		msb->index = im_amiMSBfirst() ? 0 : msb->width - 1;
		msb->repeat = in->Bands;

		if( vips_bandfmt_isuint( in->BandFmt ) )
			func = (im_wrapone_fn) byte_select;
		else
			func = (im_wrapone_fn) byte_select_flip;
	}
	else if( IM_CODING_LABQ == in->Coding )
		func = (im_wrapone_fn) msb_labq;
	else {
		im_error( "im_msb", "%s", _( "unknown coding" ) );
		return( -1 );
	}

	if( im_cp_desc( out, in ) )
		return( -1 );
	out->BandFmt = IM_BANDFMT_UCHAR;
	out->Coding = IM_CODING_NONE;

	return( im_wrapone( in, out, func, msb, NULL ) );
}

/**
 * im_msb_band:
 * @in: input image
 * @out: output image
 * @band: select this band
 *
 * Turn any integer image to a single-band 8-bit unsigned char by discarding 
 * all but the most significant byte from the selected band. 
 * Signed values are converted to unsigned by adding 128.
 *
 * This operator also works for LABQ coding.
 *
 * See also: im_msb_band().
 *
 * Returns: 0 on success, -1 on error
 */
int
im_msb_band( IMAGE *in, IMAGE *out, int band )
{
	Msb *msb;
	im_wrapone_fn func;

	if( band < 0 ) {
		im_error( "im_msb_band", "%s", _( "bad arguments" ) );
		return( -1 );
	}

	if( im_piocheck( in, out ) ||
		!(msb = IM_NEW( out, Msb )) )
		return( -1 );

	if( in->Coding == IM_CODING_NONE ) {
		if( im_check_int( "im_msb_band", in ) ) 
			return( -1 );

		if( band >= in->Bands ) {
			im_error( "im_msb_band", "%s", 
				_( "image does not have that many bands" ) );
			return( -1 );
		}

		msb->width = IM_IMAGE_SIZEOF_ELEMENT( in );
		msb->index = im_amiMSBfirst() ? 
			msb->width * band : msb->width * (band + 1) - 1;
		msb->repeat = 1;

		if( vips_bandfmt_isuint( in->BandFmt ) )
			func = (im_wrapone_fn) byte_select;
		else
			func = (im_wrapone_fn) byte_select_flip;
	}
	else if( IM_CODING_LABQ == in->Coding ) {
		if( band > 2 ) {
			im_error( "im_msb_band", "%s", 
				_( "image does not have that many bands" ) );
			return( -1 );
		}
		msb->width = 4;
		msb->repeat = 1;
		msb->index = band;

		if( band )
			func = (im_wrapone_fn) byte_select_flip;
		else
			func = (im_wrapone_fn) byte_select;
	}
	else {
		im_error( "im_msb", "%s", _( "unknown coding" ) );
		return( -1 );
	}

	if( im_cp_desc( out, in ) )
		return( -1 );
	out->BandFmt = IM_BANDFMT_UCHAR;
	out->Coding = IM_CODING_NONE;
	out->Bands = 1;

	return( im_wrapone( in, out, func, msb, NULL ) );
}
