/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---- includes ----------------------------------------------------------- */

#include "b_BasicEm/Math.h"
#include "b_BasicEm/Functions.h"
#include "b_ImageEm/UInt8Image.h"

/* ------------------------------------------------------------------------- */

/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ auxiliary functions } ---------------------------------------- */
/*                                                                           */
/* ========================================================================= */

/* ------------------------------------------------------------------------- */

/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ constructor / destructor } ----------------------------------- */
/*                                                                           */
/* ========================================================================= */

/* ------------------------------------------------------------------------- */

void bim_UInt8Image_init( struct bbs_Context* cpA,
						  struct bim_UInt8Image* ptrA )
{
	bbs_UInt8Arr_init( cpA, &ptrA->arrE );
	ptrA->widthE = 0;
	ptrA->heightE = 0;
}

/* ------------------------------------------------------------------------- */

void bim_UInt8Image_create( struct bbs_Context* cpA,
						    struct bim_UInt8Image* ptrA, 
						    uint32 widthA, 
							uint32 heightA,
 					        struct bbs_MemSeg* mspA )
{
	if( bbs_Context_error( cpA ) ) return;
	if( ptrA->arrE.arrPtrE != 0 )
	{
		bim_UInt8Image_size( cpA, ptrA, widthA, heightA );
	}
	else
	{
		bbs_UInt8Arr_create( cpA, &ptrA->arrE, widthA * heightA, mspA );
		ptrA->widthE  = widthA;
		ptrA->heightE = heightA;
	}
}
/* ------------------------------------------------------------------------- */

void bim_UInt8Image_exit( struct bbs_Context* cpA,
						  struct bim_UInt8Image* ptrA )
{
	bbs_UInt8Arr_exit( cpA, &ptrA->arrE );
	ptrA->widthE = 0;
	ptrA->heightE = 0;	
}

/* ------------------------------------------------------------------------- */

/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ operators } -------------------------------------------------- */
/*                                                                           */
/* ========================================================================= */

/* ------------------------------------------------------------------------- */

void bim_UInt8Image_copy( struct bbs_Context* cpA,
						  struct bim_UInt8Image* ptrA, 
						  const struct bim_UInt8Image* srcPtrA )
{
#ifdef DEBUG1
	if( ptrA->arrE.sizeE < srcPtrA->arrE.sizeE )
	{
		bbs_ERROR0( "void bim_UInt8Image_copy( struct bim_UInt8Image*, const struct bim_UInt8Image* ):\n"
				   "Unsufficient allocated memory in destination image" );		
		return;
	}
#endif
	ptrA->widthE = srcPtrA->widthE;
	ptrA->heightE = srcPtrA->heightE;
	bbs_UInt8Arr_copy( cpA, &ptrA->arrE, &srcPtrA->arrE );
}

/* ------------------------------------------------------------------------- */

flag bim_UInt8Image_equal( struct bbs_Context* cpA,
						   const struct bim_UInt8Image* ptrA, 
						   const struct bim_UInt8Image* srcPtrA )
{
	if( ptrA->widthE != srcPtrA->widthE ) return FALSE;
	if( ptrA->heightE != srcPtrA->heightE ) return FALSE;
	return bbs_UInt8Arr_equal( cpA, &ptrA->arrE, &srcPtrA->arrE );
}

/* ------------------------------------------------------------------------- */

/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ query functions } -------------------------------------------- */
/*                                                                           */
/* ========================================================================= */

/* ------------------------------------------------------------------------- */

uint32 bim_UInt8Image_checkSum( struct bbs_Context* cpA,
							    const struct bim_UInt8Image* ptrA )
{
	uint32 sumL =0 ;
	uint32 iL;
	uint32 sizeL = ptrA->arrE.sizeE;
	const uint8* ptrL = ptrA->arrE.arrPtrE;
	for( iL =0; iL < sizeL; iL++ )
	{
		sumL += *ptrL++;
	}
	return sumL;
}

/* ------------------------------------------------------------------------- */

/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ modify functions } ------------------------------------------- */
/*                                                                           */
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
	
void bim_UInt8Image_assignExternalImage( struct bbs_Context* cpA,
										 struct bim_UInt8Image* ptrA, 
										 struct bim_UInt8Image* srcPtrA )
{
	struct bbs_MemSeg sharedSegL = bbs_MemSeg_createShared( cpA, srcPtrA->arrE.arrPtrE, ( srcPtrA->widthE * srcPtrA->heightE ) / 2 );

	if( ptrA->arrE.arrPtrE != 0 )
	{
		bbs_ERROR0( "void bim_UInt8Image_assignExternalImage( ... ): image was already created once" );
		return;
	}

	bim_UInt8Image_create( cpA, ptrA, 
					       srcPtrA->widthE, 
						   srcPtrA->heightE,
						   &sharedSegL );
}

/* ------------------------------------------------------------------------- */
	
void bim_UInt8Image_size( struct bbs_Context* cpA,
						  struct bim_UInt8Image* ptrA, 
						  uint32 widthA, 
						  uint32 heightA )
{
	if( ptrA->arrE.allocatedSizeE < widthA * heightA )
	{
		bbs_ERROR0( "void bim_UInt8Image_size( struct bim_UInt8Image*, uint32 sizeA ):\n"
				   "Unsufficient allocated memory" );
		return;
	}
	bbs_UInt8Arr_size( cpA, &ptrA->arrE, widthA * heightA );
	ptrA->widthE  = widthA;
	ptrA->heightE = heightA;
}

/* ------------------------------------------------------------------------- */
	
/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ I/O } -------------------------------------------------------- */
/*                                                                           */
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
	
uint32 bim_UInt8Image_memSize( struct bbs_Context* cpA,
							   const struct bim_UInt8Image* ptrA )
{
	return  bbs_SIZEOF16( uint32 )
		  + bbs_SIZEOF16( uint32 ) /* version */
		  + bbs_SIZEOF16( ptrA->widthE ) 
		  + bbs_SIZEOF16( ptrA->heightE )
		  + bbs_UInt8Arr_memSize( cpA, &ptrA->arrE ); 
}

/* ------------------------------------------------------------------------- */
	
uint32 bim_UInt8Image_memWrite( struct bbs_Context* cpA,
							    const struct bim_UInt8Image* ptrA, 
								uint16* memPtrA )
{
	uint32 memSizeL = bim_UInt8Image_memSize( cpA, ptrA );
	memPtrA += bbs_memWrite32( &memSizeL, memPtrA );
	memPtrA += bbs_memWriteUInt32( bim_UINT8_IMAGE_VERSION, memPtrA );
	memPtrA += bbs_memWrite32( &ptrA->widthE, memPtrA );
	memPtrA += bbs_memWrite32( &ptrA->heightE, memPtrA );
	bbs_UInt8Arr_memWrite( cpA, &ptrA->arrE, memPtrA );
	return memSizeL;
}

/* ------------------------------------------------------------------------- */
	
uint32 bim_UInt8Image_memRead( struct bbs_Context* cpA,
							   struct bim_UInt8Image* ptrA, 
							   const uint16* memPtrA,
 					           struct bbs_MemSeg* mspA )
{
	uint32 memSizeL, versionL, widthL, heightL;
	if( bbs_Context_error( cpA ) ) return 0;
	memPtrA += bbs_memRead32( &memSizeL, memPtrA );
	memPtrA += bbs_memReadVersion32( cpA, &versionL, bim_UINT8_IMAGE_VERSION, memPtrA );
	memPtrA += bbs_memRead32( &widthL, memPtrA );
	memPtrA += bbs_memRead32( &heightL, memPtrA );

	ptrA->widthE  = widthL;
	ptrA->heightE = heightL;
	bbs_UInt8Arr_memRead( cpA, &ptrA->arrE, memPtrA, mspA );

	if( memSizeL != bim_UInt8Image_memSize( cpA, ptrA ) )
	{
		bbs_ERR0( bbs_ERR_CORRUPT_DATA, "uint32 bim_UInt8Image_memRead( const struct bim_UInt8Image* ptrA, const void* memPtrA ):\n"
                   "size mismatch" ); 
		return 0;
	}
	return memSizeL;
}

/* ------------------------------------------------------------------------- */
	
/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ exec functions } --------------------------------------------- */
/*                                                                           */
/* ========================================================================= */

/* ------------------------------------------------------------------------- */

void bim_UInt8Image_setAllPixels( struct bbs_Context* cpA,
								  struct bim_UInt8Image* ptrA, 
								  uint8 valueA )
{
	long iL;
	uint8* ptrL = ptrA->arrE.arrPtrE;
	for( iL = ptrA->widthE * ptrA->heightE; iL > 0; iL-- )
	{
		*ptrL++ = valueA;
	}
}

/* ------------------------------------------------------------------------- */

/**
			|				|				|				|
			|	(loop x1)	|	(loop x2)	|	(loop x3)	|
			o------------->-o------------>--o------------->-o
			|				|				|				|
			|				|				|				|
			|				|				|				|
			|				|				|				|
	( sectionL->x1E, sectionL->y1E )		|				|
---------o-	R-------------------------------|----------------
		 |	|				|				|				|
		 |	|				|				|				|
		 |	|				|				|				|
		 |	|				|				|				|
   (loop y1)|				|				|				|
		 |	|				|				|				|
		 V	|				|				|				|
		 |	|				|( 0, 0 )		|				|		X
---------o------------------I------------------------------------------------->
		 |	|				|				|				|
		 |	|				|				|				|
		 |	|				|				|				|
		 |	|				|				|				|
		 |	|				|				|				|
   (loop y2)|				|				|				|
		 |	|				|				|				|
		 |	|				|				|				|
		 |	|				|				|				|
		 V	|				|				|				|
		 |	|				|				|				|
---------o------------------|---------------I				|
		 |	|				|		( srcPtrA->widthE, srcPtrA->heightE )
		 |	|				|								|
		 |	|				|								|
		 |	|				|								|
		 |	|				|								|
		 |	|				|								|
   (loop y3)|				|								|
		 |	|				|								|
		 |	|				|								|
		 V	|				|								|
		 |	|				|								|
---------o--------------------------------------------------R
							|				( sectionL->x2E, sectionL->y2E )
							|
						  Y	|
							|
							|
							V

  To understand how the algorithm work refer to the diagram above.
  The image boundaries are indicated by letter "I" ( 0, 0 ) to ( srcPtrA->widthE, srcPtrA->heightE )
  The rectangle boundaries are indicated by letter "R" ( sectionPtrA->x1E, sectionPtrA->y1E ) to ( sectionPtrA->x2E, sectionPtrA->y2E )

  In the above example the intersection of the image and the rectange is
  ( 0, 0 ), ( srcPtrA->widthE, srcPtrA->heightE )

  The size of the destination image is always ( ( sectionL->x2E, sectionL->y2E ) - ( sectionL->x1E, sectionL->y1E ) )

  All coordinates are assumed to be relative to the original image.

  1. parse all pixels in "loop y1"
	1.a. parse all pixels in "loop x1"
	1.b. parse all pixels in "loop x2"
	1.c. parse all pixels in "loop x3"
  2. parse all pixels in "loop y2"
	2.a. parse all pixels in "loop x1"
	2.b. parse all pixels in "loop x2"
	2.c. parse all pixels in "loop x3"
  3. parse all pixels in "loop y3"
	3.a. parse all pixels in "loop x1"
	3.b. parse all pixels in "loop x2"
	3.c. parse all pixels in "loop x3"

*/

/** copies a section of given image */
void bim_UInt8Image_copySection( struct bbs_Context* cpA,
								 struct bim_UInt8Image* ptrA, 
								 const struct bim_UInt8Image* srcPtrA, 
								 const struct bts_Int16Rect* sectionPtrA )
{

	uint8* srcPixelPtrL;
	uint8* dstPixelPtrL;
	int32 yIndexL;
	int32 xIndexL;

	struct bts_Int16Rect srcImageSubSectionL;
	struct bts_Int16Rect sectionL;

	/* make sure that the rectangle passed is correct, in case the x2 < x1 or y2 < y1, swap them */
	sectionL.x1E = bbs_min( sectionPtrA->x1E, sectionPtrA->x2E );
	sectionL.x2E = bbs_max( sectionPtrA->x1E, sectionPtrA->x2E );
	sectionL.y1E = bbs_min( sectionPtrA->y1E, sectionPtrA->y2E );
	sectionL.y2E = bbs_max( sectionPtrA->y1E, sectionPtrA->y2E );

	/* find the intersection betweem the rectangle and the image, the image always starts at 0,0 */
	srcImageSubSectionL.x1E = bbs_max( 0, sectionL.x1E );
	srcImageSubSectionL.y1E = bbs_max( 0, sectionL.y1E );
	srcImageSubSectionL.x2E = bbs_min( ( int32 ) srcPtrA->widthE, sectionL.x2E );
	srcImageSubSectionL.y2E = bbs_min( ( int32 ) srcPtrA->heightE, sectionL.y2E );

	/* If the image and the rectangle do not intersect in X direction, set the intersecting rectangle to the image coordinates */
	if( srcImageSubSectionL.x2E < srcImageSubSectionL.x1E )
	{
		srcImageSubSectionL.x1E = 0;
		srcImageSubSectionL.x2E = srcPtrA->widthE;
	}
	/* do the same as above in the Y direction */
	if( srcImageSubSectionL.y2E < srcImageSubSectionL.y1E )
	{
		srcImageSubSectionL.y1E = 0;
		srcImageSubSectionL.y2E = srcPtrA->heightE;
	}

	/* set size, and allocate required memory for the destination image if required */
	bim_UInt8Image_size( cpA, ptrA, sectionL.x2E - sectionL.x1E, sectionL.y2E - sectionL.y1E );

	/* get the pointer to the destination image */
	dstPixelPtrL = ptrA->arrE.arrPtrE;

	/* 1. parse all pixels in "loop y1" */
	for( yIndexL = sectionL.y1E; yIndexL < srcImageSubSectionL.y1E && yIndexL < sectionL.y2E; yIndexL++ )
	{
		/* move to the first pixel that needs to be copied. */
		srcPixelPtrL = srcPtrA->arrE.arrPtrE;

		/* 1.a. parse all pixels in "loop x1" */
		for( xIndexL = sectionL.x1E; xIndexL < srcImageSubSectionL.x1E && xIndexL < sectionL.x2E; xIndexL++ )
		{
			*dstPixelPtrL++ = *srcPixelPtrL;
		}
		/* 1.b. parse all pixels in "loop x2" */
		for( ; xIndexL < srcImageSubSectionL.x2E && xIndexL < sectionL.x2E; xIndexL++ )
		{
			*dstPixelPtrL++ = *srcPixelPtrL++;
		}
		srcPixelPtrL--;
		/* 1.c. parse all pixels in "loop x3" */
		for( ; xIndexL < sectionL.x2E; xIndexL++ )
		{
			*dstPixelPtrL++ = *srcPixelPtrL;
		}
	}
	/* 2. parse all pixels in "loop y2" */
	for( ; yIndexL < srcImageSubSectionL.y2E && yIndexL < sectionL.y2E; yIndexL++ )
	{
		/* move to the first pixel that needs to be copied. */
		srcPixelPtrL = srcPtrA->arrE.arrPtrE + yIndexL * srcPtrA->widthE + srcImageSubSectionL.x1E;

		/* 2.a. parse all pixels in "loop x1" */
		for( xIndexL = sectionL.x1E; xIndexL < srcImageSubSectionL.x1E && xIndexL < sectionL.x2E; xIndexL++ )
		{
			*dstPixelPtrL++ = *srcPixelPtrL;
		}
		/* 2.b. parse all pixels in "loop x2" */
		for( ; xIndexL < srcImageSubSectionL.x2E && xIndexL < sectionL.x2E; xIndexL++ )
		{
			*dstPixelPtrL++ = *srcPixelPtrL++;
		}
		srcPixelPtrL--;
		/* 2.c. parse all pixels in "loop x3" */
		for( ; xIndexL < sectionL.x2E; xIndexL++ )
		{
			*dstPixelPtrL++ = *srcPixelPtrL;
		}
	}
	/* 3. parse all pixels in "loop y3" */
	for( ; yIndexL < sectionL.y2E; yIndexL++ )
	{
		srcPixelPtrL = srcPtrA->arrE.arrPtrE + ( srcImageSubSectionL.y2E - 1 ) * srcPtrA->widthE + srcImageSubSectionL.x1E;

		/* 3.a. parse all pixels in "loop x1" */
		for( xIndexL = sectionL.x1E; xIndexL < srcImageSubSectionL.x1E && xIndexL < sectionL.x2E; xIndexL++ )
		{
			*dstPixelPtrL++ = *srcPixelPtrL;
		}
		/* 3.b. parse all pixels in "loop x3" */
		for( ; xIndexL < srcImageSubSectionL.x2E && xIndexL < sectionL.x2E; xIndexL++ )
		{
			*dstPixelPtrL++ = *srcPixelPtrL++;
		}
		srcPixelPtrL--;
		/* 3.c. parse all pixels in "loop x3" */
		for( ; xIndexL < sectionL.x2E; xIndexL++ )
		{
			*dstPixelPtrL++ = *srcPixelPtrL;
		}
	}

}

/* ------------------------------------------------------------------------- */

/**

 
		M-------------------------------------------------------M
		|				|						|				|
		|				|						|				|
		|				|						|				|
		|				|						|				|
		|	region x0y0	|		region x1y0		|	region x2y0	|
		|				|						|				|
		|				|						|				|
		|				|						|				|
		|---------------I-----------------------I---------------|
		|				|						|				|
		|				|						|				|
		|				|						|				|
		|				|						|				|
		|				|						|				|
		|				|						|				|
		|	region x0y1	|		region x1y1		|	region x2y1	|
		|				|						|				|
		|				|						|				|
		|				|						|				|
		|				|						|				|
		|				|						|				|
		|				|						|				|
		|				|						|				|
		|---------------I-----------------------I---------------|
		|				|						|				|
		|				|						|				|
		|				|						|				|
		|				|						|				|
		|	region x0y2	|		region x1y2		|	region x2y2	|
		|				|						|				|
		|				|						|				|
		|				|						|				|
		M-------------------------------------------------------M


  To see how the code is organized. Refer to the diagram above.
  Assume the original image after applying the tranzformations(translation, rotation and scaling) is "O" 
	(boundaries of the image are shown above bounded by the letter 'O').
  This image is being Warped to the area "M" (boundaries of this area are bounded by the letter 'M').
  
	Refer to the source code below to point to the loop that maps pixels in the particular region.

 */

/** applies affine linear warping to pixels positions of imageA before copying the into *ptrA */
void bim_UInt8Image_warpOffs( struct bbs_Context* cpA,
						  struct bim_UInt8Image* ptrA, 
						  const struct bim_UInt8Image* srcPtrA, 
						  int32 xOffsA,
						  int32 yOffsA,
						  const struct bts_Flt16Alt2D* altPtrA,
			              int32 resultWidthA,
			              int32 resultHeightA )
{
	long srcWidthL = srcPtrA->widthE;
	long srcHeightL = srcPtrA->heightE;
	
	struct bts_Flt16Alt2D invAlt2DL;
	
	uint8* dstPtrL;
	const uint8* ulPtrL = srcPtrA->arrE.arrPtrE;
	const uint8* urPtrL = ulPtrL + srcWidthL - 1;
	const uint8* llPtrL = ulPtrL + ( srcHeightL - 1 ) * srcWidthL;
	const uint8* lrPtrL = llPtrL + srcWidthL - 1;
	
	uint32 iL, jL;
	int32 shiftL;

	const uint16 bbpL = 16;
	int32 maxInt32Value8bbpL  = 0x7FFFFFFF;

	/* The bbp for all these variables is the same as bbpL */
	int32 mxxL;
	int32 mxyL;
	int32 myxL;
	int32 myyL;

	int32 txL;
	int32 tyL;

	int32 xL;
	int32 yL;

	bim_UInt8Image_size( cpA, ptrA, resultWidthA, resultHeightA );
	dstPtrL = ptrA->arrE.arrPtrE;
	
	/* compute inverse */
	invAlt2DL = bts_Flt16Alt2D_inverted( altPtrA );
	
	if( srcWidthL == 0 || srcHeightL == 0 )
	{
		bim_UInt8Image_size( cpA, ptrA, srcWidthL, srcHeightL );
		bbs_ERROR2( "Size of output image is %d/%d", srcWidthL, srcHeightL );
		return;
	}

	/* align Matrix and Vector to 8 bits bbp */
	shiftL = invAlt2DL.matE.bbpE - bbpL;
	if( shiftL >= 0 )
	{
		mxxL = invAlt2DL.matE.xxE >> shiftL;
		mxyL = invAlt2DL.matE.xyE >> shiftL;
		myxL = invAlt2DL.matE.yxE >> shiftL;
		myyL = invAlt2DL.matE.yyE >> shiftL;
	}
	else
	{
		/* Check for overflow since we are left shifting. */
		maxInt32Value8bbpL >>= -shiftL;
		if( invAlt2DL.matE.xxE > maxInt32Value8bbpL ||
			invAlt2DL.matE.xyE > maxInt32Value8bbpL ||
			invAlt2DL.matE.yxE > maxInt32Value8bbpL ||
			invAlt2DL.matE.yyE > maxInt32Value8bbpL )
		{
			/* Overflow error */
			bbs_ERROR5( "The values in the transformation matrix cause overflow during bitshift\n%d, %d,\n%d, %d\n"
						"The maximum allowed value is %d", 
						invAlt2DL.matE.xxE >> invAlt2DL.matE.bbpE,
						invAlt2DL.matE.xyE >> invAlt2DL.matE.bbpE,
						invAlt2DL.matE.yxE >> invAlt2DL.matE.bbpE,
						invAlt2DL.matE.yyE >> invAlt2DL.matE.bbpE,
						maxInt32Value8bbpL >> ( bbpL - ( -shiftL ) ) );
			return;
		}

		mxxL = invAlt2DL.matE.xxE << -shiftL;
		mxyL = invAlt2DL.matE.xyE << -shiftL;
		myxL = invAlt2DL.matE.yxE << -shiftL;
		myyL = invAlt2DL.matE.yyE << -shiftL;
		maxInt32Value8bbpL <<= -shiftL;
	}

	/* invAlt2DL.matE.bbpE = bbpL; nonsense! */

	shiftL = invAlt2DL.vecE.bbpE - bbpL;
	if( shiftL >= 0 )
	{
		txL  = invAlt2DL.vecE.xE >> shiftL;
		tyL  = invAlt2DL.vecE.yE >> shiftL;
	}
	else
	{
		/* Check for overflow since we are left shifting. */
		maxInt32Value8bbpL >>= -shiftL;
		if(	invAlt2DL.vecE.xE  > maxInt32Value8bbpL ||
			invAlt2DL.vecE.yE  > maxInt32Value8bbpL )
		{
			/* Overflow error */
			bbs_ERROR3( "The values in the vector cause overflow during bitshift\n%d, %d,\n"
						"The maximum allowed value is %d", 
						invAlt2DL.vecE.xE >> invAlt2DL.vecE.bbpE,
						invAlt2DL.vecE.yE >> invAlt2DL.vecE.bbpE,
						maxInt32Value8bbpL >> ( bbpL - ( -shiftL ) ) );
			return;
		}
		txL  = invAlt2DL.vecE.xE << -shiftL;
		tyL  = invAlt2DL.vecE.yE << -shiftL;
		maxInt32Value8bbpL <<= -shiftL;
	}

	/* invAlt2DL.vecE.bbpE = bbpL; nonsense! */

	/* adjust offset */
	txL += xOffsA << bbpL;
	tyL += yOffsA << bbpL;

	/* For each destination pixel find the correspoding source pixel by applying the inverse transformation */
	for( jL = 0; jL < ptrA->heightE; jL++ )
	{
		xL = txL + mxyL * jL;
		yL = tyL + myyL * jL;
		for( iL = 0; iL < ptrA->widthE; iL++ )
		{
			const uint16 bbpLby2L = bbpL / 2;
			const int32 oneL = 0x00000001 << bbpLby2L;
			const int32 fractionOnlyL = 0xFFFFFFFF >> ( 32 - bbpL );

			/* The bbp for all these variables is the same as bbpLby2L */
			int32 f2xL;
			int32 f2yL;
			int32 f1xL;
			int32 f1yL;

			/* always whole numbers with a bbp of 0 */
			int32 kL;
			int32 lL;

			/* The bbpE for these variables is bbpLby2L */
			int32 valL;

			/* Get the whole numbers only and make the bbp 0. */
			kL = xL >> bbpL;
			lL = yL >> bbpL;

			/* fraction of destination pixel in the next source pixel */
			f2xL = ( xL & fractionOnlyL ) >> bbpLby2L;
			f2yL = ( yL & fractionOnlyL ) >> bbpLby2L;
			/* fraction of destination pixel in the current source pixel */
			f1xL = oneL - f2xL;
			f1yL = oneL - f2yL;

			/* increment values for next loop */
			xL += mxxL;
			yL += myxL;

			if( lL < 0 )
			{
				if( kL < 0 )
				{
					/* handle all pixels in region x0y0 */
					*dstPtrL++ = *ulPtrL;
				}
				else if( kL >= srcWidthL - 1 )
				{
					/* handle all pixels in region x2y0 */
					*dstPtrL++ = *urPtrL;
				}
				else
				{
					/* handle all pixels in region x1y0 */
					/* The bbp has shifted left by bbpLby2L */
					valL =  *( ulPtrL + kL ) * f1xL + *( ulPtrL + kL + 1 ) * f2xL;
					*dstPtrL++ = valL >> bbpLby2L;
				}
			} /* if( lL < 0 ) */
			else if( lL >= srcHeightL - 1 )
			{
				if( kL < 0 )
				{
					/* handle all pixels in region x0y2 */
					*dstPtrL++ = *llPtrL;
				}
				else if( kL >= srcWidthL - 1 )
				{
					/* handle all pixels in region x2y2 */
					*dstPtrL++ = *lrPtrL;
				}
				else
				{
					/* handle all pixels in region x1y2 */
					/* The bbp has shifted left by bbpLby2L */
					valL =   *( llPtrL + kL ) * f1xL + *( llPtrL +  kL + 1 ) * f2xL;
					*dstPtrL++ = valL >> bbpLby2L;
				}
			} /* if( lL >= srcHeightL - 1 ) */
			else
			{
				const uint8* ptr1L;
				const uint8* ptr2L;

				ptr1L = ulPtrL + lL * srcWidthL;
				/* point to the pixel in the same column */
				ptr2L = ptr1L + srcWidthL;
				if( kL < 0 )
				{
					/* handle all pixels in region x0y1 */
					/* The bbp has shifted left by bbpLby2L */
					valL = *ptr1L * f1yL + *ptr2L * f2yL ;
					*dstPtrL++ = valL >> bbpLby2L;
				}
				else if( kL >= srcWidthL - 1 )
				{
					/* handle all pixels in region x2y1 */
					/* The bbp has shifted left by bbpLby2L */
					valL =  *( ptr1L + srcWidthL - 1 ) * f1yL + *( ptr2L  + srcWidthL - 1 ) * f2yL;
					*dstPtrL++ = valL >> bbpLby2L;
				}
				else
				{
					/* assuming that bbpL = bbpLby2 * 2 */
					/* The bbp for these variables is bbpLby2L */
					int32 v1L;
					int32 v2L;
					/* The bbp for these variables is bbpL */
					const int32 halfL = 0x00000001 << ( bbpL - 1 );
	
					/* handle all pixels in region x1y1 */
					/* The bbp has shifted left by bbpLby2L */
					v1L = *( ptr1L + kL ) * f1xL + *( ptr1L + kL + 1 ) * f2xL;
					v2L = *( ptr2L + kL ) * f1xL + *( ptr2L + kL + 1 ) * f2xL;
					/* The bbp has shifted left again by bbpLby2L */
					/* adding the half to round off the resulting value */
					valL = v1L * f1yL + v2L * f2yL + halfL;
					*dstPtrL++ = valL >> bbpL;
				}
			}
		} /* iL loop */
	} /* jL loop */

}

/* ------------------------------------------------------------------------- */

void bim_UInt8Image_warp( struct bbs_Context* cpA,
						  struct bim_UInt8Image* ptrA, 
						  const struct bim_UInt8Image* srcPtrA, 
						  const struct bts_Flt16Alt2D* altPtrA,
			              int32 resultWidthA,
			              int32 resultHeightA )
{
	bim_UInt8Image_warpOffs( cpA, ptrA, srcPtrA, 0, 0, altPtrA, resultWidthA, resultHeightA );
}

/* ========================================================================= */