#include "c4d_general.h"
#include "c4d_shader.h"
#include "c4d_memory.h"

#include	"matrixutil.h"
#include "matrixrotate.h"

typedef	LONG	TYPE_apply_matrix_effect( MATRIX_REF emr, void **src_rows, void *dst, LONG width );

struct MATRIX_PRIVATE
{

	LONG	color_space;
	LONG	no_components;
	WEIGHT_FMATRIX_MAX	effect_matrix;

	TYPE_apply_matrix_effect	*apply_effect;

};

static void	change_effect_matrix( const WEIGHT_FMATRIX *input_matrix, WEIGHT_FMATRIX *effect_matrix, SReal opacity, Bool scale_matrix, LReal angle );

static void	matrix_line8( MATRIX_PRIVATE *emr, UCHAR **src_rows, UCHAR *dst, LONG width );
static void	matrix_block8_q3( MATRIX_PRIVATE *emr, UCHAR **src_rows, UCHAR *dst, LONG width );
static void	matrix_block8_q5( MATRIX_PRIVATE *emr, UCHAR **src_rows, UCHAR *dst, LONG width );
static void	matrix_block8_q7( MATRIX_PRIVATE *emr, UCHAR **src_rows, UCHAR *dst, LONG width );

static void	matrix_line_alpha8( MATRIX_PRIVATE *emr, UCHAR **src_rows, UCHAR *dst, LONG width );
static void	matrix_block_alpha8_q3( MATRIX_PRIVATE *emr, UCHAR **src_rows, UCHAR *dst, LONG width );
static void	matrix_block_alpha8_q5( MATRIX_PRIVATE *emr, UCHAR **src_rows, UCHAR *dst, LONG width );
static void	matrix_block_alpha8_q7( MATRIX_PRIVATE *emr, UCHAR **src_rows, UCHAR *dst, LONG width );

//----------------------------------------------------------------------------------------
// Create a matrix object and set up the proper functions
// Function result:		matrix object
// input_matrix:			convolution matrix
// color_space:				color space of the pixel data (used for alpha support, ...)
// px_format:					pixel format (bits per component, number of components, ..)
// opacity:						0.0: result of the convolution is transparent 1.0: result of the convolution completly replaces the source pixel
// scale_matrix:			FALSE: ignore overflows TRUE: scale matrix elements so that the sum is 1.0 (unless they sum to 0)
// angle:							rotation angle of the matrix (0 ... 2pi)
//----------------------------------------------------------------------------------------
MATRIX_REF  new_effect_matrix( const WEIGHT_FMATRIX *input_matrix, LONG color_space, ULONG px_format, SReal opacity, Bool scale_matrix, LReal angle )
{
	MATRIX_PRIVATE	*emr;
	
	emr = (MATRIX_PRIVATE *) GeAlloc( sizeof( MATRIX_PRIVATE ));
	if ( emr )
	{
		LONG	matrix_rows;
		LONG	matrix_columns;
		
		emr->color_space = color_space;
		emr->no_components = get_PX_CMPNTS( px_format );

		change_effect_matrix( input_matrix, (WEIGHT_FMATRIX *) &emr->effect_matrix, opacity, scale_matrix, angle );

		matrix_rows = emr->effect_matrix.no_matrix_rows;
		matrix_columns = emr->effect_matrix.no_matrix_columns;

		if ( color_space & CSPACE_ALPHA_FLAG )									// take care of alpha?
		{
			emr->apply_effect = (TYPE_apply_matrix_effect *) matrix_line_alpha8;
			if ( matrix_rows == matrix_columns )									// square matrix?
			{
				switch ((int) matrix_rows )
				{
					case	3:	emr->apply_effect = (TYPE_apply_matrix_effect *) matrix_block_alpha8_q3;	break;
					case	5:	emr->apply_effect = (TYPE_apply_matrix_effect *) matrix_block_alpha8_q5;	break;
					case	7:	emr->apply_effect = (TYPE_apply_matrix_effect *) matrix_block_alpha8_q7;	break;
				}
			}
		}
		else																										// color space without alpha
		{
			emr->apply_effect = (TYPE_apply_matrix_effect *) matrix_line8;
			if ( matrix_rows == matrix_columns )									// square matrix?
			{
				switch ((int) matrix_rows )
				{
					case	3:	emr->apply_effect = (TYPE_apply_matrix_effect *) matrix_block8_q3;	break;
					case	5:	emr->apply_effect = (TYPE_apply_matrix_effect *) matrix_block8_q5;	break;
					case	7:	emr->apply_effect = (TYPE_apply_matrix_effect *) matrix_block8_q7;	break;
				}
			}
		}
	}

	return((MATRIX_REF) emr );
}

LONG	delete_effect_matrix( MATRIX_REF emr )
{
	if ( emr )
		GeFree( emr );

	return( 0 );
}

void	 update_effect_matrix( MATRIX_REF emr, LONG color_space, ULONG px_format )
{
	if ( emr )
	{
		((MATRIX_PRIVATE *) emr )->color_space = color_space;
		((MATRIX_PRIVATE *) emr )->no_components = get_PX_CMPNTS( px_format );
	}
}


LONG	apply_matrix_effect( MATRIX_REF emr, void **src_rows, void *dst, LONG width )
{
	((MATRIX_PRIVATE *) emr )->apply_effect( emr, src_rows, dst, width );
	return( 0 );
}

LONG	get_effect_matrix_info( MATRIX_REF _emr, LONG *no_columns, LONG *no_rows, LONG *x_origin, LONG *y_origin )
{
	MATRIX_PRIVATE	*emr;
	
	emr = (MATRIX_PRIVATE *) _emr;

	if ( no_columns )
		*no_columns = emr->effect_matrix.no_matrix_columns;

	if ( no_rows )
		*no_rows = emr->effect_matrix.no_matrix_rows;

	if ( x_origin )
		*x_origin = emr->effect_matrix.x_origin;

	if ( y_origin )
		*y_origin = emr->effect_matrix.y_origin;

	return( 0 );
}

static void	change_effect_matrix( const WEIGHT_FMATRIX *input_matrix, WEIGHT_FMATRIX *effect_matrix, SReal opacity, Bool scale_matrix, LReal angle )
{
#define	MIN_VALUE	0.00001																		// cutoff value for scaling

	LONG	no_elements;
	LONG	i;
	LONG	j;
	const SReal	*src_value;
	SReal	*dst_value;
	SReal	matrix_sum;
	SReal	v;
	
	*effect_matrix = *input_matrix;														// copy the header

	src_value = input_matrix->matrix_values;	
	dst_value = effect_matrix->matrix_values;	
	matrix_sum = 0;

	for ( j = 0; j < effect_matrix->no_matrix_rows; j++ )
	{
		for ( i = 0; i < effect_matrix->no_matrix_columns; i++ )
		{
			v = *src_value++;
			*dst_value++ = v;
			matrix_sum += v;
		}
	}
	
	if ( angle )																							// rotate matrix?
	{
		rotate_matrix( input_matrix, effect_matrix, angle );

		if ( scale_matrix && ( matrix_sum >= MIN_VALUE ))				// rotation might have produced some rounding errors
		{
			matrix_sum = 0;
			dst_value = effect_matrix->matrix_values;							// therefore build the sum again

			for ( j = 0; j < effect_matrix->no_matrix_rows; j++ )
			{
				for ( i = 0; i < effect_matrix->no_matrix_columns; i++ )
					matrix_sum += *dst_value++;
			}
		}
	}
	
	if ( scale_matrix )																				// scale the matrix to avoid cutting off color values
	{
		if ( matrix_sum < 0 )
			matrix_sum = - matrix_sum;

		if ( matrix_sum < MIN_VALUE )														// avoid division by zero (take rounding errors into account)
			matrix_sum = 1.0;
	}
	else																											// no scaling, allow overflow
		matrix_sum = 1.0;
	
	no_elements = effect_matrix->no_matrix_rows * effect_matrix->no_matrix_columns;
	dst_value = effect_matrix->matrix_values;	
	for ( i = no_elements; i > 0; i-- )												// scale and blend matrix elements
	{
		v = ( *dst_value / matrix_sum ) * opacity;
		*dst_value++ = v;
	}
	
	dst_value = effect_matrix->matrix_values;	
	dst_value += effect_matrix->y_origin * effect_matrix->no_matrix_columns;
	dst_value += effect_matrix->x_origin;
	v = 1.0 - opacity;																			// add amount of the source pixel
	*dst_value += v;

#undef	MIN_MVALUE
}

//----------------------------------------------------------------------------------------
// General matrix handling for pixels with 8 bits per component
// Function result:		-
// emr:								matrix data
// src_rows:					array with pointers to the source lines
// dst:								pointer to the dst line
// no_components:			number of color components per pixel
//----------------------------------------------------------------------------------------
static void	matrix_line8( MATRIX_PRIVATE *emr, UCHAR **src_rows, UCHAR *dst, LONG width )
{
	WEIGHT_FMATRIX *matrix;
	LONG	no_components;
	LONG	x_offset;
		
	no_components = emr->no_components;
	matrix = (WEIGHT_FMATRIX *) &emr->effect_matrix;

	for ( x_offset = 0; width > 0; width-- )
	{
		LONG	c;

		for ( c = 0; c < no_components; c++, x_offset++ )
		{
			LONG	my;
			SReal	*matrix_values;
			SReal	n;

			n = 0;
			matrix_values = matrix->matrix_values;
			
			for ( my = 0; my < matrix->no_matrix_rows; my++ )
			{
				UCHAR	*src;
				LONG	mx;

				src = src_rows[my] + x_offset;											// select the right color component

				for ( mx = matrix->no_matrix_columns; mx > 0; mx-- )	// weigh the components of a source line
				{
					n += *matrix_values++ * (SReal) *src;
					src += no_components;															// next pixel
				}
			}

			if ( n < 0.0 )																				// clamp component if the sum of the matrix elements is not 1.0
				n = 0.0;
			if ( n > 255.0 )
				n = 255.0;
			
			*dst++ = (UCHAR) ( n + 0.5 );
		}
	}
}

// 3*3 matrix
static void	matrix_block8_q3( MATRIX_PRIVATE *emr, UCHAR **src_rows, UCHAR *dst, LONG width )
{
	WEIGHT_FMATRIX *matrix;
	LONG	no_components;
	LONG	x_offset;
		
	no_components = emr->no_components;
	matrix = (WEIGHT_FMATRIX *) &emr->effect_matrix;
	
	for ( x_offset = 0; width > 0; width-- )
	{
		LONG	c;

		for ( c = 0; c < no_components; c++, x_offset++ )
		{
			SReal	*matrix_values;
			SReal	n;
			UCHAR	*src;

			n = 0;
			matrix_values = matrix->matrix_values;
			
			src = src_rows[0] + x_offset;													// select the right color component

			n += *matrix_values++ * (SReal) *src;									// weigh the components of a source line
			src += no_components;																	// next pixel
			n += *matrix_values++ * (SReal) *src;									// weigh the components of a source line
			src += no_components;																	// next pixel
			n += *matrix_values++ * (SReal) *src;									// weigh the components of a source line

			src = src_rows[1] + x_offset;													// next line, select the right color component

			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								

			src = src_rows[2] + x_offset;												

			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								

			if ( n < 0.0 )																				// clamp component if the sum of the matrix elements is not 1.0
				n = 0.0;
			if ( n > 255.0 )
				n = 255.0;
			
			*dst++ = (UCHAR) ( n + 0.5 );
		}
	}
}

// 5*5 matrix
static void	matrix_block8_q5( MATRIX_PRIVATE *emr, UCHAR **src_rows, UCHAR *dst, LONG width )
{
	WEIGHT_FMATRIX *matrix;
	LONG	no_components;
	LONG	x_offset;
		
	no_components = emr->no_components;
	matrix = (WEIGHT_FMATRIX *) &emr->effect_matrix;
	
	for ( x_offset = 0; width > 0; width-- )
	{
		LONG	c;

		for ( c = 0; c < no_components; c++, x_offset++ )
		{
			SReal	*matrix_values;
			SReal	n;
			UCHAR	*src;

			n = 0;
			matrix_values = matrix->matrix_values;
			
			src = src_rows[0] + x_offset;													// select the right color component

			n += *matrix_values++ * (SReal) *src;									// weigh the components of a source line
			src += no_components;																	// next pixel
			n += *matrix_values++ * (SReal) *src;									// weigh the components of a source line
			src += no_components;																	// next pixel
			n += *matrix_values++ * (SReal) *src;									// weigh the components of a source line
			src += no_components;																	// next pixel
			n += *matrix_values++ * (SReal) *src;									// weigh the components of a source line
			src += no_components;																	// next pixel
			n += *matrix_values++ * (SReal) *src;									// weigh the components of a source line

			src = src_rows[1] + x_offset;													// next line, select the right color component

			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								

			src = src_rows[2] + x_offset;												

			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								

			src = src_rows[3] + x_offset;												

			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								

			src = src_rows[4] + x_offset;												

			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								

			if ( n < 0.0 )																				// clamp component if the sum of the matrix elements is not 1.0
				n = 0.0;
			if ( n > 255.0 )
				n = 255.0;
			
			*dst++ = (UCHAR) ( n + 0.5 );
		}
	}
}

// 7*7 matrix
static void	matrix_block8_q7( MATRIX_PRIVATE *emr, UCHAR **src_rows, UCHAR *dst, LONG width )
{
	WEIGHT_FMATRIX *matrix;
	LONG	no_components;
	LONG	x_offset;
		
	no_components = emr->no_components;
	matrix = (WEIGHT_FMATRIX *) &emr->effect_matrix;
	
	for ( x_offset = 0; width > 0; width-- )
	{
		LONG	c;

		for ( c = 0; c < no_components; c++, x_offset++ )
		{
			SReal	*matrix_values;
			SReal	n;
			UCHAR	*src;

			n = 0;
			matrix_values = matrix->matrix_values;
			
			src = src_rows[0] + x_offset;													// select the right color component

			n += *matrix_values++ * (SReal) *src;									// weigh the components of a source line
			src += no_components;																	// next pixel
			n += *matrix_values++ * (SReal) *src;									// weigh the components of a source line
			src += no_components;																	// next pixel
			n += *matrix_values++ * (SReal) *src;									// weigh the components of a source line
			src += no_components;																	// next pixel
			n += *matrix_values++ * (SReal) *src;									// weigh the components of a source line
			src += no_components;																	// next pixel
			n += *matrix_values++ * (SReal) *src;									// weigh the components of a source line
			src += no_components;																	// next pixel
			n += *matrix_values++ * (SReal) *src;									// weigh the components of a source line
			src += no_components;																	// next pixel
			n += *matrix_values++ * (SReal) *src;									// weigh the components of a source line

			src = src_rows[1] + x_offset;													// next line, select the right color component

			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								

			src = src_rows[2] + x_offset;												

			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								

			src = src_rows[3] + x_offset;												

			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								

			src = src_rows[4] + x_offset;												

			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								

			src = src_rows[5] + x_offset;												

			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								

			src = src_rows[6] + x_offset;												

			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								
			src += no_components;																
			n += *matrix_values++ * (SReal) *src;								

			if ( n < 0.0 )																				// clamp component if the sum of the matrix elements is not 1.0
				n = 0.0;
			if ( n > 255.0 )
				n = 255.0;
			
			*dst++ = (UCHAR) ( n + 0.5 );
		}
	}
}


//----------------------------------------------------------------------------------------
// Blur line with alpha channel
// Function result:		-
// src_rows:					array with pointers to the source lines
// dst:								pointer to the dst line
// no_components:			number of color components per pixel
// matrix:						
//----------------------------------------------------------------------------------------
static void	matrix_line_alpha8( MATRIX_PRIVATE *emr, UCHAR **src_rows, UCHAR *dst, LONG width )
{
	WEIGHT_FMATRIX *matrix;
	LONG	no_components;
	LONG	x_offset;
		
	no_components = emr->no_components;
	matrix = (WEIGHT_FMATRIX *) &emr->effect_matrix;
	
	for ( x_offset = 0; width > 0; width-- )
	{
		LONG	c;
		LONG	my;
		SReal	*matrix_values;
		SReal	new_alpha;
		SReal	n;

		n = 0;
		matrix_values = matrix->matrix_values;

		for ( my = 0; my < matrix->no_matrix_rows; my++ )
		{
			UCHAR	*src;
			LONG	mx;

			src = src_rows[my] + x_offset;

			for ( mx = matrix->no_matrix_columns; mx > 0; mx-- )	// weigh the components of a source line
			{
				n += *matrix_values++ * (SReal) *src;
				src += no_components;																// next pixel
			}
		}

		new_alpha = n;
		if ( n < 0.0 )																					// clamp component if the sum of the matrix elements is not 1.0
			n = 0.0;
		if ( n > 255.0 )
			n = 255.0;
		
		*dst++ = (UCHAR) ( n + 0.5 );

		if ( new_alpha == 0.0 )																	// is the matrixred pixel invisible?
			new_alpha = 1.0;																			// avoid division by zero

		for ( c = 1, x_offset++; c < no_components; c++, x_offset++ )	// weigh every color component with its alpha and matrix value
		{
			n = 0;
			matrix_values = matrix->matrix_values;

			for ( my = 0; my < matrix->no_matrix_rows; my++ )
			{
				UCHAR	*src;
				UCHAR	*alpha;
				LONG	mx;

				src = src_rows[my] + x_offset;											// select the right color component
				alpha = src - c;

				for ( mx = matrix->no_matrix_columns; mx > 0; mx-- )	// weigh the components of a source line
				{
					n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
					alpha += no_components;														// next pixel
					src += no_components;															// next pixel
				}
			}

			n /= new_alpha;																				// new color component of the matrixred pixel (has to take the sum of the alphas into account)
			if ( n < 0.0 )																				// clamp component if the sum of the matrix elements is not 1.0
				n = 0.0;
			if ( n > 255.0 )
				n = 255.0;

			*dst++ = (UCHAR) ( n + 0.5 );
		}
	}
}

// 3*3 matrix
static void	matrix_block_alpha8_q3( MATRIX_PRIVATE *emr, UCHAR **src_rows, UCHAR *dst, LONG width )
{
	WEIGHT_FMATRIX *matrix;
	LONG	no_components;
	LONG	x_offset;
		
	no_components = emr->no_components;
	matrix = (WEIGHT_FMATRIX *) &emr->effect_matrix;

	for ( x_offset = 0; width > 0; width-- )
	{
		LONG	c;
		SReal	*matrix_values;
		SReal	new_alpha;
		SReal	n;
		UCHAR	*src;

		n = 0;
		matrix_values = matrix->matrix_values;

		src = src_rows[0] + x_offset;												// select the right color component

		n += *matrix_values++ * (SReal) *src;								// weigh the components of a source line
		src += no_components;																// next pixel
		n += *matrix_values++ * (SReal) *src;								// weigh the components of a source line
		src += no_components;																// next pixel
		n += *matrix_values++ * (SReal) *src;								// weigh the components of a source line

		src = src_rows[1] + x_offset;												// next line, select the right color component

		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								

		src = src_rows[2] + x_offset;												

		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								

		new_alpha = n;
		if ( n < 0.0 )																					// clamp component if the sum of the matrix elements is not 1.0
			n = 0.0;
		if ( n > 255.0 )
			n = 255.0;
		
		*dst++ = (UCHAR) ( n + 0.5 );

		if ( new_alpha == 0.0 )																	// is the matrixred pixel invisible?
			new_alpha = 1.0;																			// avoid division by zero

		for ( c = 1, x_offset++; c < no_components; c++, x_offset++ )		// weigh every color component with its alpha and matrix value
		{
			UCHAR	*src;
			UCHAR	*alpha;

			n = 0;
			matrix_values = matrix->matrix_values;

			src = src_rows[0] + x_offset;											// select the right color component
			alpha = src - c;

			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														// next pixel
			src += no_components;															// next pixel
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														// next pixel
			src += no_components;															// next pixel
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;

			src = src_rows[1] + x_offset;											// next line, select the right color component
			alpha = src - c;

			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;

			src = src_rows[2] + x_offset;											
			alpha = src - c;

			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;

			n /= new_alpha;																				// new color component of the matrixred pixel (has to take the sum of the alphas into account)
			if ( n < 0.0 )																				// clamp component if the sum of the matrix elements is not 1.0
				n = 0.0;
			if ( n > 255.0 )
				n = 255.0;

			*dst++ = (UCHAR) ( n + 0.5 );
		}
	}
}

// 5*5 matrix
static void	matrix_block_alpha8_q5( MATRIX_PRIVATE *emr, UCHAR **src_rows, UCHAR *dst, LONG width )
{
	WEIGHT_FMATRIX *matrix;
	LONG	no_components;
	LONG	x_offset;
		
	no_components = emr->no_components;
	matrix = (WEIGHT_FMATRIX *) &emr->effect_matrix;

	for ( x_offset = 0; width > 0; width-- )
	{
		LONG	c;
		SReal	*matrix_values;
		SReal	new_alpha;
		SReal	n;
		UCHAR	*src;

		n = 0;
		matrix_values = matrix->matrix_values;

		src = src_rows[0] + x_offset;												// select the right color component

		n += *matrix_values++ * (SReal) *src;								// weigh the components of a source line
		src += no_components;																// next pixel
		n += *matrix_values++ * (SReal) *src;								// weigh the components of a source line
		src += no_components;																// next pixel
		n += *matrix_values++ * (SReal) *src;								// weigh the components of a source line
		src += no_components;																// next pixel
		n += *matrix_values++ * (SReal) *src;								// weigh the components of a source line
		src += no_components;																// next pixel
		n += *matrix_values++ * (SReal) *src;								// weigh the components of a source line

		src = src_rows[1] + x_offset;												// next line, select the right color component

		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								

		src = src_rows[2] + x_offset;												

		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								

		src = src_rows[3] + x_offset;												

		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								

		src = src_rows[4] + x_offset;												

		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								

		new_alpha = n;
		if ( n < 0.0 )																					// clamp component if the sum of the matrix elements is not 1.0
			n = 0.0;
		if ( n > 255.0 )
			n = 255.0;
		
		*dst++ = (UCHAR) ( n + 0.5 );

		if ( new_alpha == 0.0 )																	// is the matrixed pixel invisible?
			new_alpha = 1.0;																			// avoid division by zero

		for ( c = 1, x_offset++; c < no_components; c++, x_offset++ )		// weigh every color component with its alpha and matrix value
		{
			UCHAR	*src;
			UCHAR	*alpha;

			n = 0;
			matrix_values = matrix->matrix_values;

			src = src_rows[0] + x_offset;											// select the right color component
			alpha = src - c;

			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														// next pixel
			src += no_components;															// next pixel
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														// next pixel
			src += no_components;															// next pixel
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														// next pixel
			src += no_components;															// next pixel
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														// next pixel
			src += no_components;															// next pixel
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;

			src = src_rows[1] + x_offset;											// next line, select the right color component
			alpha = src - c;

			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;

			src = src_rows[2] + x_offset;											
			alpha = src - c;

			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;

			src = src_rows[3] + x_offset;											
			alpha = src - c;

			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;

			src = src_rows[4] + x_offset;											
			alpha = src - c;

			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;

			n /= new_alpha;																				// new color component of the matrixred pixel (has to take the sum of the alphas into account)
			if ( n < 0.0 )																				// clamp component if the sum of the matrix elements is not 1.0
				n = 0.0;
			if ( n > 255.0 )
				n = 255.0;

			*dst++ = (UCHAR) ( n + 0.5 );
		}
	}
}

// 7*7 matrix
static void	matrix_block_alpha8_q7( MATRIX_PRIVATE *emr, UCHAR **src_rows, UCHAR *dst, LONG width )
{
	WEIGHT_FMATRIX *matrix;
	LONG	no_components;
	LONG	x_offset;
		
	no_components = emr->no_components;
	matrix = (WEIGHT_FMATRIX *) &emr->effect_matrix;

	for ( x_offset = 0; width > 0; width-- )
	{
		LONG	c;
		SReal	*matrix_values;
		SReal	new_alpha;
		SReal	n;
		UCHAR	*src;

		n = 0;
		matrix_values = matrix->matrix_values;

		src = src_rows[0] + x_offset;												// select the right color component

		n += *matrix_values++ * (SReal) *src;								// weigh the components of a source line
		src += no_components;																// next pixel
		n += *matrix_values++ * (SReal) *src;								// weigh the components of a source line
		src += no_components;																// next pixel
		n += *matrix_values++ * (SReal) *src;								// weigh the components of a source line
		src += no_components;																// next pixel
		n += *matrix_values++ * (SReal) *src;								// weigh the components of a source line
		src += no_components;																// next pixel
		n += *matrix_values++ * (SReal) *src;								// weigh the components of a source line
		src += no_components;																// next pixel
		n += *matrix_values++ * (SReal) *src;								// weigh the components of a source line
		src += no_components;																// next pixel
		n += *matrix_values++ * (SReal) *src;								// weigh the components of a source line

		src = src_rows[1] + x_offset;												// next line, select the right color component

		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								

		src = src_rows[2] + x_offset;												

		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								

		src = src_rows[3] + x_offset;												

		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								

		src = src_rows[4] + x_offset;												

		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								

		src = src_rows[5] + x_offset;												

		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								

		src = src_rows[6] + x_offset;												

		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								
		src += no_components;																
		n += *matrix_values++ * (SReal) *src;								

		new_alpha = n;
		if ( n < 0.0 )																					// clamp component if the sum of the matrix elements is not 1.0
			n = 0.0;
		if ( n > 255.0 )
			n = 255.0;
		
		*dst++ = (UCHAR) ( n + 0.5 );

		if ( new_alpha == 0.0 )																	// is the matrixred pixel invisible?
			new_alpha = 1.0;																			// avoid division by zero

		for ( c = 1, x_offset++; c < no_components; c++, x_offset++ )		// weigh every color component with its alpha and matrix value
		{
			UCHAR	*src;
			UCHAR	*alpha;

			n = 0;
			matrix_values = matrix->matrix_values;

			src = src_rows[0] + x_offset;											// select the right color component
			alpha = src - c;

			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														// next pixel
			src += no_components;															// next pixel
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														// next pixel
			src += no_components;															// next pixel
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														// next pixel
			src += no_components;															// next pixel
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														// next pixel
			src += no_components;															// next pixel
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														// next pixel
			src += no_components;															// next pixel
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														// next pixel
			src += no_components;															// next pixel
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;

			src = src_rows[1] + x_offset;											// next line, select the right color component
			alpha = src - c;

			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;

			src = src_rows[2] + x_offset;											
			alpha = src - c;

			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;

			src = src_rows[3] + x_offset;											
			alpha = src - c;

			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;

			src = src_rows[4] + x_offset;											
			alpha = src - c;

			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;

			src = src_rows[5] + x_offset;											
			alpha = src - c;

			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;

			src = src_rows[6] + x_offset;											
			alpha = src - c;

			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;
			alpha += no_components;														
			src += no_components;															
			n += *matrix_values++ * (SReal) *src * (SReal) *alpha;

			n /= new_alpha;																				// new color component of the matrixred pixel (has to take the sum of the alphas into account)
			if ( n < 0.0 )																				// clamp component if the sum of the matrix elements is not 1.0
				n = 0.0;
			if ( n > 255.0 )
				n = 255.0;

			*dst++ = (UCHAR) ( n + 0.5 );
		}
	}
}
