/*****************************************************************************/
// Copyright 2006-2008 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE:  Adobe permits you to use, modify, and distribute this file in
// accordance with the terms of the Adobe license agreement accompanying it.
/*****************************************************************************/

/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_pixel_buffer.cpp#1 $ */ 
/* $DateTime: 2012/05/30 13:28:51 $ */
/* $Change: 832332 $ */
/* $Author: tknoll $ */

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

#include "dng_pixel_buffer.h"

#include "dng_bottlenecks.h"
#include "dng_exceptions.h"
#include "dng_flags.h"
#include "dng_safe_arithmetic.h"
#include "dng_tag_types.h"
#include "dng_tag_values.h"
#include "dng_utils.h"

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

namespace {

bool SafeUint32ToInt32Mult(uint32 arg1, uint32 arg2, int32 *result) {
	uint32 uint32_result;
	return SafeUint32Mult(arg1, arg2, &uint32_result) &&
		ConvertUint32ToInt32(uint32_result, result);
}

} // namespace

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

void OptimizeOrder (const void *&sPtr,
					void *&dPtr,
					uint32 sPixelSize,
					uint32 dPixelSize,
					uint32 &count0,
					uint32 &count1,
					uint32 &count2,
					int32 &sStep0,
					int32 &sStep1,
					int32 &sStep2,
					int32 &dStep0,
					int32 &dStep1,
					int32 &dStep2)
	{
	
	uint32 step0;
	uint32 step1;
	uint32 step2;
	
	// Optimize the order for the data that is most spread out.
							   
	uint32 sRange = Abs_int32 (sStep0) * (count0 - 1) +
					Abs_int32 (sStep1) * (count1 - 1) +
					Abs_int32 (sStep2) * (count2 - 1);
							   
	uint32 dRange = Abs_int32 (dStep0) * (count0 - 1) +
					Abs_int32 (dStep1) * (count1 - 1) +
					Abs_int32 (dStep2) * (count2 - 1);
							   
	if (dRange >= sRange)
		{						
	
		if (dStep0 < 0)
			{
			
			sPtr = (const void *)
				   (((const uint8 *) sPtr) + (int32)(count0 - 1) * sStep0 * (int32)sPixelSize);
				   
			dPtr = (void *)
				   (((uint8 *) dPtr) + (int32)(count0 - 1) * dStep0 * (int32)dPixelSize);
				   
			sStep0 = -sStep0;
			dStep0 = -dStep0;
			
			}
		
		if (dStep1 < 0)
			{
			
			sPtr = (const void *)
				   (((const uint8 *) sPtr) + (int32)(count1 - 1) * sStep1 * (int32)sPixelSize);
				   
			dPtr = (void *)
				   (((uint8 *) dPtr) + (int32)(count1 - 1) * dStep1 * (int32)dPixelSize);
				   
			sStep1 = -sStep1;
			dStep1 = -dStep1;
			
			}
		
		if (dStep2 < 0)
			{
			
			sPtr = (const void *)
				   (((const uint8 *) sPtr) + (int32)(count2 - 1) * sStep2 * (int32)sPixelSize);
				   
			dPtr = (void *)
				   (((uint8 *) dPtr) + (int32)(count2 - 1) * dStep2 * (int32)dPixelSize);
				   
			sStep2 = -sStep2;
			dStep2 = -dStep2;
			
			}
		
		step0 = (uint32) dStep0;
		step1 = (uint32) dStep1;
		step2 = (uint32) dStep2;
		
		}
		
	else
		{						
	
		if (sStep0 < 0)
			{
			
			sPtr = (const void *)
				   (((const uint8 *) sPtr) + (int32)(count0 - 1) * sStep0 * (int32)sPixelSize);
				   
			dPtr = (void *)
				   (((uint8 *) dPtr) + (int32)(count0 - 1) * dStep0 * (int32)dPixelSize);
				   
			sStep0 = -sStep0;
			dStep0 = -dStep0;
			
			}
		
		if (sStep1 < 0)
			{
			
			sPtr = (const void *)
				   (((const uint8 *) sPtr) + (int32)(count1 - 1) * sStep1 * (int32)sPixelSize);
				   
			dPtr = (void *)
				   (((uint8 *) dPtr) + (int32)(count1 - 1) * dStep1 * (int32)dPixelSize);
				   
			sStep1 = -sStep1;
			dStep1 = -dStep1;
			
			}
		
		if (sStep2 < 0)
			{
			
			sPtr = (const void *)
				   (((const uint8 *) sPtr) + (int32)(count2 - 1) * sStep2 * (int32)sPixelSize);
				   
			dPtr = (void *)
				   (((uint8 *) dPtr) + (int32)(count2 - 1) * dStep2 * (int32)dPixelSize);
				   
			sStep2 = -sStep2;
			dStep2 = -dStep2;
			
			}
		
		step0 = (uint32) sStep0;
		step1 = (uint32) sStep1;
		step2 = (uint32) sStep2;
		
		}
	
	if (count0 == 1) step0 = 0xFFFFFFFF;
	if (count1 == 1) step1 = 0xFFFFFFFF;
	if (count2 == 1) step2 = 0xFFFFFFFF;
	
	uint32 index0;
	uint32 index1;
	uint32 index2;
	
	if (step0 >= step1)
		{
		
		if (step1 >= step2)
			{
			index0 = 0;
			index1 = 1;
			index2 = 2;
			}
			
		else if (step2 >= step0)
			{
			index0 = 2;
			index1 = 0;
			index2 = 1;
			}
			
		else
			{
			index0 = 0;
			index1 = 2;
			index2 = 1;
			}
		
		}
		
	else
		{
		
		if (step0 >= step2)
			{
			index0 = 1;
			index1 = 0;
			index2 = 2;
			}
			
		else if (step2 >= step1)
			{
			index0 = 2;
			index1 = 1;
			index2 = 0;
			}
			
		else
			{
			index0 = 1;
			index1 = 2;
			index2 = 0;
			}
		
		}
		
	uint32 count [3];
	
	count [0] = count0;
	count [1] = count1;
	count [2] = count2;
	
	count0 = count [index0];
	count1 = count [index1];
	count2 = count [index2];
	
	int32 step [3];
	
	step [0] = sStep0;
	step [1] = sStep1;
	step [2] = sStep2;
	
	sStep0 = step [index0];
	sStep1 = step [index1];
	sStep2 = step [index2];
	
	step [0] = dStep0;
	step [1] = dStep1;
	step [2] = dStep2;
	
	dStep0 = step [index0];
	dStep1 = step [index1];
	dStep2 = step [index2];
	
	if (sStep0 == ((int32) count1) * sStep1 &&
		dStep0 == ((int32) count1) * dStep1)
		{
		count1 *= count0;
		count0 = 1;
		}
	
	if (sStep1 == ((int32) count2) * sStep2 &&
		dStep1 == ((int32) count2) * dStep2)
		{
		count2 *= count1;
		count1 = 1;
		}
	
	}

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

void OptimizeOrder (const void *&sPtr,
					uint32 sPixelSize,
					uint32 &count0,
					uint32 &count1,
					uint32 &count2,
					int32 &sStep0,
					int32 &sStep1,
					int32 &sStep2)
	{
	
	void *dPtr = NULL;
	
	int32 dStep0 = sStep0;
	int32 dStep1 = sStep1;
	int32 dStep2 = sStep2;
	
	OptimizeOrder (sPtr,
				   dPtr,
				   sPixelSize,
				   sPixelSize,
				   count0,
				   count1,
				   count2,
				   sStep0,
				   sStep1,
				   sStep2,
				   dStep0,
				   dStep1,
				   dStep2);
	
	}

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

void OptimizeOrder (void *&dPtr,
					uint32 dPixelSize,
					uint32 &count0,
					uint32 &count1,
					uint32 &count2,
					int32 &dStep0,
					int32 &dStep1,
					int32 &dStep2)
	{
	
	const void *sPtr = NULL;
	
	int32 sStep0 = dStep0;
	int32 sStep1 = dStep1;
	int32 sStep2 = dStep2;
	
	OptimizeOrder (sPtr,
				   dPtr,
				   dPixelSize,
				   dPixelSize,
				   count0,
				   count1,
				   count2,
				   sStep0,
				   sStep1,
				   sStep2,
				   dStep0,
				   dStep1,
				   dStep2);
	
	}

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

dng_pixel_buffer::dng_pixel_buffer ()

	:	fArea       ()
	,	fPlane      (0)
	,	fPlanes     (1)
	,	fRowStep    (1)
	,	fColStep    (1)
	,	fPlaneStep  (1)
	,	fPixelType  (ttUndefined)
	,	fPixelSize  (0)
	,	fData       (NULL)
	,	fDirty      (true)
	
	{
	
	}

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

dng_pixel_buffer::dng_pixel_buffer (const dng_rect &area,
									uint32 plane,
									uint32 planes,
									uint32 pixelType,
									uint32 planarConfiguration,
									void *data)

	:	fArea       (area)
	,	fPlane      (plane)
	,	fPlanes     (planes)
	,	fRowStep    (0)
	,	fColStep    (0)
	,	fPlaneStep  (0)
	,	fPixelType  (pixelType)
	,	fPixelSize  (TagTypeSize(pixelType))
	,	fData       (data)
	,	fDirty      (true)
	
	{
	
	const char *overflowMessage = "Arithmetic overflow in pixel buffer setup";
	
	// Initialize fRowStep, fColStep and fPlaneStep according to the desired
	// pixel layout.
	switch (planarConfiguration)
		{
		case pcInterleaved:
			fPlaneStep = 1;
			if (!ConvertUint32ToInt32 (fPlanes, &fColStep) ||
				!SafeUint32ToInt32Mult (fArea.W(), fPlanes, &fRowStep))
				{
				ThrowMemoryFull (overflowMessage);
				}
			break;
		case pcPlanar:
			fColStep = 1;
			// Even though we've hardened dng_rect::W() to guarantee that it
			// will never return a result that's out of range for an int32,
			// we still protect the conversion for defense in depth.
			if (!ConvertUint32ToInt32 (fArea.W(), &fRowStep) ||
				!SafeUint32ToInt32Mult (fArea.H(), fArea.W(), &fPlaneStep))
				{
				ThrowMemoryFull (overflowMessage);
				}
			break;
		case pcRowInterleaved:
		case pcRowInterleavedAlign16:
			{
			fColStep = 1;
			uint32 planeStepUint32;
			if (planarConfiguration == pcRowInterleaved)
				{
				planeStepUint32 = fArea.W();
				}
			else
				{
				if (!RoundUpForPixelSize (fArea.W(), fPixelSize,
										  &planeStepUint32))
					{
					ThrowMemoryFull (overflowMessage);
					}
				}
			if (!ConvertUint32ToInt32 (planeStepUint32, &fPlaneStep) ||
				!SafeUint32ToInt32Mult (planeStepUint32, fPlanes, &fRowStep))
				{
				ThrowMemoryFull (overflowMessage);
				}
			break;
			}
		default:
			ThrowProgramError ("Invalid value for 'planarConfiguration'");
			break;
		}
	
	}

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

dng_pixel_buffer::dng_pixel_buffer (const dng_pixel_buffer &buffer)

	:	fArea       (buffer.fArea)
	,	fPlane      (buffer.fPlane)
	,	fPlanes     (buffer.fPlanes)
	,	fRowStep    (buffer.fRowStep)
	,	fColStep    (buffer.fColStep)
	,	fPlaneStep  (buffer.fPlaneStep)
	,	fPixelType  (buffer.fPixelType)
	,	fPixelSize  (buffer.fPixelSize)
	,	fData       (buffer.fData)
	,	fDirty      (buffer.fDirty)
	
	{
	
	}
							
/*****************************************************************************/

dng_pixel_buffer & dng_pixel_buffer::operator= (const dng_pixel_buffer &buffer)
	{
	
	fArea       = buffer.fArea;
	fPlane      = buffer.fPlane;
	fPlanes     = buffer.fPlanes;
	fRowStep    = buffer.fRowStep;
	fColStep    = buffer.fColStep;
	fPlaneStep  = buffer.fPlaneStep;
	fPixelType  = buffer.fPixelType;
	fPixelSize  = buffer.fPixelSize;
	fPixelType  = buffer.fPixelType;
	fData       = buffer.fData;
	fDirty      = buffer.fDirty;
	
	return *this;
	
	}

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

dng_pixel_buffer::~dng_pixel_buffer ()
	{
	
	}
							
/*****************************************************************************/

#if qDebugPixelType

void dng_pixel_buffer::CheckPixelType (uint32 pixelType) const
	{
	
	if (fPixelType != pixelType)
		{
		
		DNG_REPORT ("Pixel type access mismatch");
		
		}
	
	}

#endif

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

uint32 dng_pixel_buffer::PixelRange () const
	{
	
	switch (fPixelType)
		{
		
		case ttByte:
		case ttSByte:
			{
			return 0x0FF;
			}
			
		case ttShort:
		case ttSShort:
			{
			return 0x0FFFF;
			}
			
		case ttLong:
		case ttSLong:
			{
			return 0xFFFFFFFF;
			}
			
		default:
			break;
			
		}
	
	return 0;
	
	}
							
/*****************************************************************************/

void dng_pixel_buffer::SetConstant (const dng_rect &area,
									uint32 plane,
									uint32 planes,
									uint32 value)
	{
	
	uint32 rows = area.H ();
	uint32 cols = area.W ();
	
	void *dPtr = DirtyPixel (area.t,
					    	 area.l,
					    	 plane);
					    
	int32 dRowStep   = fRowStep;
	int32 dColStep   = fColStep;
	int32 dPlaneStep = fPlaneStep;
	
	OptimizeOrder (dPtr,
				   fPixelSize,
				   rows,
				   cols,
				   planes,
				   dRowStep,
				   dColStep,
				   dPlaneStep);
				   
	switch (fPixelSize)
		{
		
		case 1:
			{
			
			if (rows == 1 && cols == 1 && dPlaneStep == 1 && value == 0)
				{
				
				DoZeroBytes (dPtr, planes);
				
				}
				
			else
				{
				
				DoSetArea8 ((uint8 *) dPtr,
							(uint8) value,
							rows,
							cols,
							planes,
							dRowStep,
							dColStep,
							dPlaneStep);
						 
				}
				
			break;
			
			}
				   
		case 2:
			{
			
			if (rows == 1 && cols == 1 && dPlaneStep == 1 && value == 0)
				{
				
				DoZeroBytes (dPtr, planes << 1);
				
				}
				
			else
				{
				
				DoSetArea16 ((uint16 *) dPtr,
							 (uint16) value,
							 rows,
							 cols,
							 planes,
							 dRowStep,
							 dColStep,
							 dPlaneStep);
						 
				}
				
			break;
			
			}
				   
		case 4:
			{
			
			if (rows == 1 && cols == 1 && dPlaneStep == 1 && value == 0)
				{
				
				DoZeroBytes (dPtr, planes << 2);
				
				}
				
			else
				{
				
				DoSetArea32 ((uint32 *) dPtr,
							 value,
							 rows,
							 cols,
							 planes,
							 dRowStep,
							 dColStep,
							 dPlaneStep);
						 
				}
				
			break;
			
			}
			
		default:
			{
			
			ThrowNotYetImplemented ();
			
			}
			
		}
				   
	}
		
/*****************************************************************************/

void dng_pixel_buffer::SetZero (const dng_rect &area,
					   			uint32 plane,
					   			uint32 planes)
	{
					   
	uint32 value = 0;
				   
	switch (fPixelType)
		{
		
		case ttByte:
		case ttShort:
		case ttLong:
		case ttFloat:
			{
			break;
			}
			
		case ttSShort:
			{
			value = 0x8000;
			break;
			}

		default:
			{
			
			ThrowNotYetImplemented ();
			
			}
			
		}
		
	SetConstant (area,
				 plane,
				 planes,
				 value);
				   
	}
		
/*****************************************************************************/

void dng_pixel_buffer::CopyArea (const dng_pixel_buffer &src,
					   			 const dng_rect &area,
					   			 uint32 srcPlane,
					   			 uint32 dstPlane,
					   			 uint32 planes)
	{
	
	uint32 rows = area.H ();
	uint32 cols = area.W ();
	
	const void *sPtr = src.ConstPixel (area.t,
								  	   area.l,
								  	   srcPlane);
								  
	void *dPtr = DirtyPixel (area.t,
					   		 area.l,
					    	 dstPlane);
	
	int32 sRowStep   = src.fRowStep;
	int32 sColStep   = src.fColStep;
	int32 sPlaneStep = src.fPlaneStep;
	
	int32 dRowStep   = fRowStep;
	int32 dColStep   = fColStep;
	int32 dPlaneStep = fPlaneStep;
	
	OptimizeOrder (sPtr,
				   dPtr,
				   src.fPixelSize,
				   fPixelSize,
				   rows,
				   cols,
				   planes,
				   sRowStep,
				   sColStep,
				   sPlaneStep,
				   dRowStep,
				   dColStep,
				   dPlaneStep);
				   
	if (fPixelType == src.fPixelType)
		{
		
		if (rows == 1 && cols == 1 && sPlaneStep == 1 && dPlaneStep == 1)
			{
			
			DoCopyBytes (sPtr,
						 dPtr, 
						 planes * fPixelSize);
			
			}
			
		else switch (fPixelSize)
			{
			
			case 1:
				{
				
				DoCopyArea8 ((const uint8 *) sPtr,
							 (uint8 *) dPtr,
							 rows,
							 cols,
							 planes,
							 sRowStep,
							 sColStep,
							 sPlaneStep,
							 dRowStep,
							 dColStep,
							 dPlaneStep);
				
				break;
				
				}
				
			case 2:
				{
				
				DoCopyArea16 ((const uint16 *) sPtr,
							  (uint16 *) dPtr,
							  rows,
							  cols,
							  planes,
							  sRowStep,
							  sColStep,
							  sPlaneStep,
							  dRowStep,
							  dColStep,
							  dPlaneStep);
				
				break;
				
				}
				
			case 4:
				{
				
				DoCopyArea32 ((const uint32 *) sPtr,
							  (uint32 *) dPtr,
							  rows,
							  cols,
							  planes,
							  sRowStep,
							  sColStep,
							  sPlaneStep,
							  dRowStep,
							  dColStep,
							  dPlaneStep);
				
				break;
				
				}
				
			default:
				{
				
				ThrowNotYetImplemented ();
				
				}
				
			}
		
		}
		
	else if (src.fPixelType == ttByte)
		{
		
		switch (fPixelType)
			{
			
			case ttShort:
				{
				
				DoCopyArea8_16 ((const uint8 *) sPtr,
							    (uint16 *) dPtr,
							    rows,
							    cols,
							    planes,
							    sRowStep,
							    sColStep,
							    sPlaneStep,
							    dRowStep,
							    dColStep,
							    dPlaneStep);
				
				break;
				
				}
				
			case ttSShort:
				{
				
				DoCopyArea8_S16 ((const uint8 *) sPtr,
							     (int16 *) dPtr,
							     rows,
							     cols,
							     planes,
							     sRowStep,
							     sColStep,
							     sPlaneStep,
							     dRowStep,
							     dColStep,
							     dPlaneStep);
				
				break;
				
				}
				
			case ttLong:
				{
				
				DoCopyArea8_32 ((const uint8 *) sPtr,
							    (uint32 *) dPtr,
							    rows,
							    cols,
							    planes,
							    sRowStep,
							    sColStep,
							    sPlaneStep,
							    dRowStep,
							    dColStep,
							    dPlaneStep);
				
				break;
				
				}
				
			case ttFloat:
				{
				
				DoCopyArea8_R32 ((const uint8 *) sPtr,
							     (real32 *) dPtr,
							     rows,
							     cols,
							     planes,
							     sRowStep,
							     sColStep,
							     sPlaneStep,
							     dRowStep,
							     dColStep,
							     dPlaneStep,
								 src.PixelRange ());
				
				break;
				
				}
				
			default:
				{
				
				ThrowNotYetImplemented ();
				
				}
				
			}
		
		}
		
	else if (src.fPixelType == ttShort)
		{
		
		switch (fPixelType)
			{
			
			case ttByte:
				{
				
				DoCopyArea8 (((const uint8 *) sPtr) + (qDNGBigEndian ? 1 : 0),
							 (uint8 *) dPtr,
							 rows,
							 cols,
							 planes,
							 sRowStep << 1,
							 sColStep << 1,
							 sPlaneStep << 1,
							 dRowStep,
							 dColStep,
							 dPlaneStep);
						
				break;
				
				}

			case ttSShort:
				{
				
				DoCopyArea16_S16 ((const uint16 *) sPtr,
							      (int16 *) dPtr,
							      rows,
							      cols,
							      planes,
							      sRowStep,
							      sColStep,
							      sPlaneStep,
							      dRowStep,
							      dColStep,
							      dPlaneStep);
				
				break;
				
				}
				
			case ttLong:
				{
				
				DoCopyArea16_32 ((const uint16 *) sPtr,
							     (uint32 *) dPtr,
							     rows,
							     cols,
							     planes,
							     sRowStep,
							     sColStep,
							     sPlaneStep,
							     dRowStep,
							     dColStep,
							     dPlaneStep);
				
				break;
				
				}
				
			case ttFloat:
				{
				
				DoCopyArea16_R32 ((const uint16 *) sPtr,
							      (real32 *) dPtr,
								  rows,
							      cols,
							      planes,
							      sRowStep,
							      sColStep,
							      sPlaneStep,
							      dRowStep,
							      dColStep,
							      dPlaneStep,
								  src.PixelRange ());
				
				break;
				
				}
				
			default:
				{
				
				ThrowNotYetImplemented ();
				
				}
				
			}
			
		}
		
	else if (src.fPixelType == ttSShort)
		{
		
		switch (fPixelType)
			{
			
			case ttByte:
				{
				
				DoCopyArea8 (((const uint8 *) sPtr) + (qDNGBigEndian ? 1 : 0),
							 (uint8 *) dPtr,
							 rows,
							 cols,
							 planes,
							 sRowStep << 1,
							 sColStep << 1,
							 sPlaneStep << 1,
							 dRowStep,
							 dColStep,
							 dPlaneStep);
						
				break;
				
				}

			case ttShort:
				{
				
				// Moving between signed 16 bit values and unsigned 16
				// bit values just requires toggling the sign bit.  So
				// we can use the "backwards" bottleneck.
				
				DoCopyArea16_S16 ((const uint16 *) sPtr,
							      (int16 *) dPtr,
							      rows,
							      cols,
							      planes,
							      sRowStep,
							      sColStep,
							      sPlaneStep,
							      dRowStep,
							      dColStep,
							      dPlaneStep);
				
				break;
				
				}
				
			case ttFloat:
				{
				
				DoCopyAreaS16_R32 ((const int16 *) sPtr,
								   (real32 *) dPtr,
								   rows,
							       cols,
							       planes,
								   sRowStep,
							       sColStep,
								   sPlaneStep,
							       dRowStep,
							       dColStep,
							       dPlaneStep,
								   src.PixelRange ());
				
				break;
				
				}
				
			default:
				{
				
				ThrowNotYetImplemented ();
				
				}
				
			}
			
		}
		
	else if (src.fPixelType == ttLong)
		{
		
		switch (fPixelType)
			{
			
			case ttByte:
				{
				
				DoCopyArea8 (((const uint8 *) sPtr) + (qDNGBigEndian ? 3 : 0),
							 (uint8 *) dPtr,
							 rows,
							 cols,
							 planes,
							 sRowStep << 2,
							 sColStep << 2,
							 sPlaneStep << 2,
							 dRowStep,
							 dColStep,
							 dPlaneStep);
						
				break;
				
				}

			case ttShort:
				{
				
				DoCopyArea16 (((const uint16 *) sPtr) + (qDNGBigEndian ? 1 : 0),
							  (uint16 *) dPtr,
							  rows,
							  cols,
							  planes,
							  sRowStep << 1,
							  sColStep << 1,
							  sPlaneStep << 1,
							  dRowStep,
							  dColStep,
							  dPlaneStep);
				
				break;
				
				}
				
			default:
				{
				
				ThrowNotYetImplemented ();
				
				}
				
			}
			
		}
		
	else if (src.fPixelType == ttFloat)
		{
		
		switch (fPixelType)
			{
			
			case ttByte:
				{
				
				DoCopyAreaR32_8 ((const real32 *) sPtr,
							     (uint8 *) dPtr,
							     rows,
							     cols,
							     planes,
							     sRowStep,
							     sColStep,
							     sPlaneStep,
							     dRowStep,
							     dColStep,
							     dPlaneStep,
								 PixelRange ());
						
				break;
				
				}

			case ttShort:
				{
				
				DoCopyAreaR32_16 ((const real32 *) sPtr,
							      (uint16 *) dPtr,
							      rows,
							      cols,
							      planes,
							      sRowStep,
							      sColStep,
							      sPlaneStep,
							      dRowStep,
							      dColStep,
							      dPlaneStep,
								  PixelRange ());
				
				break;
				
				}
				
			case ttSShort:
				{
				
				DoCopyAreaR32_S16 ((const real32 *) sPtr,
							       (int16 *) dPtr,
							       rows,
							       cols,
							       planes,
							       sRowStep,
							       sColStep,
							       sPlaneStep,
							       dRowStep,
							       dColStep,
							       dPlaneStep,
								   PixelRange ());
				
				break;
				
				}
				
			default:
				{
				
				ThrowNotYetImplemented ();
				
				}
				
			}
		
		}
		
	else
		{
		
		ThrowNotYetImplemented ();
		
		}
		
	}
		
/*****************************************************************************/

dng_point dng_pixel_buffer::RepeatPhase (const dng_rect &srcArea,
					   			   		 const dng_rect &dstArea)
	{
	
	int32 repeatV = srcArea.H ();
	int32 repeatH = srcArea.W ();
	
	int32 phaseV;
	int32 phaseH;
	
	if (srcArea.t >= dstArea.t)
		{
		phaseV = (repeatV - ((srcArea.t - dstArea.t) % repeatV)) % repeatV;
		}
	else
		{
		phaseV = (dstArea.t - srcArea.t) % repeatV;
		}
		
	if (srcArea.l >= dstArea.l)
		{
		phaseH = (repeatH - ((srcArea.l - dstArea.l) % repeatH)) % repeatH;
		}
	else
		{
		phaseH = (dstArea.l - srcArea.l) % repeatH;
		}
		
	return dng_point (phaseV, phaseH);
	
	}
	
/*****************************************************************************/

void dng_pixel_buffer::RepeatArea (const dng_rect &srcArea,
					   			   const dng_rect &dstArea)
	{
	
	dng_point repeat = srcArea.Size ();
	
	dng_point phase = RepeatPhase (srcArea,
								   dstArea);
			
	const void *sPtr = ConstPixel (srcArea.t,
							  	   srcArea.l,
							  	   fPlane);
							  	   
	void *dPtr = DirtyPixel (dstArea.t,
							 dstArea.l,
							 fPlane);
							 
	uint32 rows = dstArea.H ();
	uint32 cols = dstArea.W ();
		
	switch (fPixelSize)
		{
			
		case 1:
			{
			
			DoRepeatArea8 ((const uint8 *) sPtr,
						   (uint8 *) dPtr,
						   rows,
						   cols,
						   fPlanes,
						   fRowStep,
						   fColStep,
						   fPlaneStep,
						   repeat.v,
						   repeat.h,
						   phase.v,
						   phase.h);
			
			break;
			
			}
			
		case 2:
			{
			
			DoRepeatArea16 ((const uint16 *) sPtr,
					  		(uint16 *) dPtr,
					  		rows,
					  		cols,
					  		fPlanes,
					  		fRowStep,
					  		fColStep,
					  		fPlaneStep,
					  		repeat.v,
						    repeat.h,
						    phase.v,
						    phase.h);
			
			break;
			
			}
				
		case 4:
			{
			
			DoRepeatArea32 ((const uint32 *) sPtr,
					  		(uint32 *) dPtr,
					  		rows,
					  		cols,
					  		fPlanes,
					  		fRowStep,
					  		fColStep,
					  		fPlaneStep,
					  		repeat.v,
						    repeat.h,
						    phase.v,
						    phase.h);
			
			break;
			
			}
			
		default:
			{
			
			ThrowNotYetImplemented ();
			
			}
			
		}
				
	}

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

void dng_pixel_buffer::RepeatSubArea (const dng_rect subArea,
									  uint32 repeatV,
									  uint32 repeatH)
	{
	
	if (fArea.t < subArea.t)
		{
		
		RepeatArea (dng_rect (subArea.t          , fArea.l,
							  subArea.t + repeatV, fArea.r),
					dng_rect (fArea.t            , fArea.l,
							  subArea.t          , fArea.r));
							  
		}
	
	if (fArea.b > subArea.b)
		{
		
		RepeatArea (dng_rect (subArea.b - repeatV, fArea.l,
							  subArea.b          , fArea.r),
					dng_rect (subArea.b          , fArea.l,
							  fArea.b            , fArea.r));
							  
		}
		
	if (fArea.l < subArea.l)
		{
		
		RepeatArea (dng_rect (fArea.t, subArea.l          ,
							  fArea.b, subArea.l + repeatH),
					dng_rect (fArea.t, fArea.l            ,
							  fArea.b, subArea.l          ));

		}
	
	if (fArea.r > subArea.r)
		{
		
		RepeatArea (dng_rect (fArea.t, subArea.r - repeatH,
							  fArea.b, subArea.r          ),
					dng_rect (fArea.t, subArea.r          ,
							  fArea.b, fArea.r            ));

		}
	
	}

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

void dng_pixel_buffer::ShiftRight (uint32 shift)
	{
	
	if (fPixelType != ttShort)
		{
		
		ThrowNotYetImplemented ();
		
		}
	
	uint32 rows = fArea.H ();
	uint32 cols = fArea.W ();
	
	uint32 planes = fPlanes;
	
	void *dPtr = DirtyPixel (fArea.t,
							 fArea.l,
							 fPlane);
	
	const void *sPtr = dPtr;
	
	int32 sRowStep   = fRowStep;
	int32 sColStep   = fColStep;
	int32 sPlaneStep = fPlaneStep;
	
	int32 dRowStep   = fRowStep;
	int32 dColStep   = fColStep;
	int32 dPlaneStep = fPlaneStep;
	
	OptimizeOrder (sPtr,
				   dPtr,
				   fPixelSize,
				   fPixelSize,
				   rows,
				   cols,
				   planes,
				   sRowStep,
				   sColStep,
				   sPlaneStep,
				   dRowStep,
				   dColStep,
				   dPlaneStep);
				   
	DoShiftRight16 ((uint16 *) dPtr,
				    rows,
				    cols,
				    planes,
				    dRowStep,
				    dColStep,
				    dPlaneStep,
				    shift);
	
	}
		
/*****************************************************************************/

void dng_pixel_buffer::FlipH ()
	{

	fData = InternalPixel (fArea.t, fArea.r - 1);

	fColStep = -fColStep;

	}

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

void dng_pixel_buffer::FlipV ()
	{
	
	fData = InternalPixel (fArea.b - 1, fArea.l);

	fRowStep = -fRowStep;

	}

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

void dng_pixel_buffer::FlipZ ()
	{

	fData = InternalPixel (fArea.t, fArea.l, fPlanes - 1);

	fPlaneStep = -fPlaneStep;

	}

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

bool dng_pixel_buffer::EqualArea (const dng_pixel_buffer &src,
								  const dng_rect &area,
								  uint32 plane,
								  uint32 planes) const
	{
	
	uint32 rows = area.H ();
	uint32 cols = area.W ();
	
	const void *sPtr = src.ConstPixel (area.t,
								  	   area.l,
								  	   plane);
								  
	const void *dPtr = ConstPixel (area.t,
								   area.l,
								   plane);
	
	int32 sRowStep   = src.fRowStep;
	int32 sColStep   = src.fColStep;
	int32 sPlaneStep = src.fPlaneStep;
	
	int32 dRowStep   = fRowStep;
	int32 dColStep   = fColStep;
	int32 dPlaneStep = fPlaneStep;

	if (fPixelType == src.fPixelType)
		{
		
		if (rows == 1 && cols == 1 && sPlaneStep == 1 && dPlaneStep == 1)
			{
			
			return DoEqualBytes (sPtr,
								 dPtr, 
								 planes * fPixelSize);
			
			}
			
		else switch (fPixelSize)
			{
			
			case 1:
				{
				
				return DoEqualArea8 ((const uint8 *) sPtr,
									 (const uint8 *) dPtr,
									 rows,
									 cols,
									 planes,
									 sRowStep,
									 sColStep,
									 sPlaneStep,
									 dRowStep,
									 dColStep,
									 dPlaneStep);
				
				break;
				
				}
				
			case 2:
				{
				
				return DoEqualArea16 ((const uint16 *) sPtr,
									  (const uint16 *) dPtr,
									  rows,
									  cols,
									  planes,
									  sRowStep,
									  sColStep,
									  sPlaneStep,
									  dRowStep,
									  dColStep,
									  dPlaneStep);

				break;
				
				}
				
			case 4:
				{
				
				return DoEqualArea32 ((const uint32 *) sPtr,
									  (const uint32 *) dPtr,
									  rows,
									  cols,
									  planes,
									  sRowStep,
									  sColStep,
									  sPlaneStep,
									  dRowStep,
									  dColStep,
									  dPlaneStep);
				
				break;
				
				}
				
			default:
				{
				
				ThrowNotYetImplemented ();

				return false;

				}
				
			}
		
		}
		
	else
		return false;

	}

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

namespace
	{

	template <typename T>
	real64 MaxDiff (const T *src1,
					int32 s1RowStep,
					int32 s1PlaneStep,
					const T *src2,
					int32 s2RowStep,
					int32 s2PlaneStep,
					uint32 rows,
					uint32 cols,
					uint32 planes)
		{

		real64 result = 0.0;

		for (uint32 plane = 0; plane < planes; plane++)
			{

			const T *src1Save = src1;
			const T *src2Save = src2;

			for (uint32 row = 0; row < rows; row++)
				{

				for (uint32 col = 0; col < cols; col++)
					{
					real64 diff = fabs ((real64)src1 [col] - src2 [col]);

					if (diff > result)
						result = diff;

					}

				src1 += s1RowStep;
				src2 += s2RowStep;

				}

			src1 = src1Save + s1PlaneStep;
			src2 = src2Save + s2PlaneStep;

			}

		return result;

		}

	template <typename T>
	real64 MaxDiff (const T *src1,
					int32 s1ColStep,
					int32 s1RowStep,
					int32 s1PlaneStep,
					const T *src2,
					int32 s2ColStep,
					int32 s2RowStep,
					int32 s2PlaneStep,
					uint32 rows,
					uint32 cols,
					uint32 planes)
		{

		if (s1ColStep == s2ColStep &&
			s1ColStep == 1)
			return MaxDiff (src1,
							s1RowStep,
							s1PlaneStep,
							src2,
							s2RowStep,
							s2PlaneStep,
							rows,
							cols,
							planes);

		real64 result = 0.0;

		for (uint32 plane = 0; plane < planes; plane++)
			{

			const T *src1Save = src1;
			const T *src2Save = src2;

			for (uint32 row = 0; row < rows; row++)
				{

				for (uint32 col = 0; col < cols; col++)
					{
					real64 diff = fabs ((real64)src1 [col * s1ColStep] - src2 [col * s2ColStep]);

					if (diff > result)
						result = diff;

					}

				src1 += s1RowStep;
				src2 += s2RowStep;

				}

			src1 = src1Save + s1PlaneStep;
			src2 = src2Save + s2PlaneStep;

			}


		return result;

		}
	}

real64 dng_pixel_buffer::MaximumDifference (const dng_pixel_buffer &rhs,
											const dng_rect &area,
											uint32 plane,
											uint32 planes) const
	{
	
	uint32 rows = area.H ();
	uint32 cols = area.W ();
	
	const void *s1Ptr = rhs.ConstPixel (area.t,
								  	    area.l,
								  	    plane);
								  
	const void *s2Ptr = ConstPixel (area.t,
								    area.l,
								    plane);
	
	int32 s1RowStep   = rhs.fRowStep;
	int32 s1ColStep   = rhs.fColStep;
	int32 s1PlaneStep = rhs.fPlaneStep;
	
	int32 s2RowStep   = fRowStep;
	int32 s2ColStep   = fColStep;
	int32 s2PlaneStep = fPlaneStep;

	if (fPixelType == rhs.fPixelType)
		{

		switch (fPixelType)
			{

			case ttByte:
				return MaxDiff ((const uint8 *)s1Ptr,
								s1ColStep,
								s1RowStep,
								s1PlaneStep,
								(const uint8 *)s2Ptr,
								s2ColStep,
								s2RowStep,
								s2PlaneStep,
								rows,
								cols,
								planes);

				break;

			case ttShort:
				return MaxDiff ((const uint16 *)s1Ptr,
								s1ColStep,
								s1RowStep,
								s1PlaneStep,
								(const uint16 *)s2Ptr,
								s2ColStep,
								s2RowStep,
								s2PlaneStep,
								rows,
								cols,
								planes);

				break;

			case ttLong:
				return MaxDiff ((const uint32 *)s1Ptr,
								s1ColStep,
								s1RowStep,
								s1PlaneStep,
								(const uint32 *)s2Ptr,
								s2ColStep,
								s2RowStep,
								s2PlaneStep,
								rows,
								cols,
								planes);

				break;

			case ttSByte:
				return MaxDiff ((const int8 *)s1Ptr,
								s1ColStep,
								s1RowStep,
								s1PlaneStep,
								(const int8 *)s2Ptr,
								s2ColStep,
								s2RowStep,
								s2PlaneStep,
								rows,
								cols,
								planes);

				break;

			case ttSShort:
				return MaxDiff ((const int16 *)s1Ptr,
								s1ColStep,
								s1RowStep,
								s1PlaneStep,
								(const int16 *)s2Ptr,
								s2ColStep,
								s2RowStep,
								s2PlaneStep,
								rows,
								cols,
								planes);

				break;

			case ttSLong:
				return MaxDiff ((const int32 *)s1Ptr,
								s1ColStep,
								s1RowStep,
								s1PlaneStep,
								(const int32 *)s2Ptr,
								s2ColStep,
								s2RowStep,
								s2PlaneStep,
								rows,
								cols,
								planes);

				break;

			case ttFloat:
				return MaxDiff ((const real32 *)s1Ptr,
								s1ColStep,
								s1RowStep,
								s1PlaneStep,
								(const real32 *)s2Ptr,
								s2ColStep,
								s2RowStep,
								s2PlaneStep,
								rows,
								cols,
								planes);

				break;

			case ttDouble:
				return MaxDiff ((const real64 *)s1Ptr,
								s1ColStep,
								s1RowStep,
								s1PlaneStep,
								(const real64 *)s2Ptr,
								s2ColStep,
								s2RowStep,
								s2PlaneStep,
								rows,
								cols,
								planes);

				break;


			default:
				{
				
				ThrowNotYetImplemented ();

				return 0.0;

				}
				
			}
		
		}
		
	else
		ThrowProgramError ("attempt to difference pixel buffers of different formats.");

	return 0.0;

	}

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