/*****************************************************************************/
// Copyright 2006-2007 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_rect.h#2 $ */ 
/* $DateTime: 2012/06/01 07:28:57 $ */
/* $Change: 832715 $ */
/* $Author: tknoll $ */

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

#ifndef __dng_rect__
#define __dng_rect__

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

#include "dng_exceptions.h"
#include "dng_point.h"
#include "dng_safe_arithmetic.h"
#include "dng_types.h"
#include "dng_utils.h"

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

class dng_rect
	{
	
	public:
	
		int32 t;
		int32 l;
		int32 b;
		int32 r;
		
	public:
		
		dng_rect ()
			:	t (0)
			,	l (0)
			,	b (0)
			,	r (0)
			{
			}
			
		// Constructs a dng_rect from the top-left and bottom-right corner.
		// Throws an exception if the resulting height or width are too large to
		// be represented as an int32. The intent of this is to protect code
		// that may be computing the height or width directly from the member
		// variables (instead of going through H() or W()).
		dng_rect (int32 tt, int32 ll, int32 bb, int32 rr)
			:	t (tt)
			,	l (ll)
			,	b (bb)
			,	r (rr)
			{
			int32 dummy;
			if (!SafeInt32Sub(r, l, &dummy) ||
				!SafeInt32Sub(b, t, &dummy))
				{
				ThrowProgramError ("Overflow in dng_rect constructor");
				}
			}
			
		dng_rect (uint32 h, uint32 w)
			:	t (0)
			,	l (0)
			{
				if (!ConvertUint32ToInt32(h, &b) ||
					!ConvertUint32ToInt32(w, &r))
					{
					ThrowProgramError ("Overflow in dng_rect constructor");
					}
			}
			
		dng_rect (const dng_point &size)
			:	t (0)
			,	l (0)
			,	b (size.v)
			,	r (size.h)
			{
			}
		
		void Clear ()
			{
			*this = dng_rect ();
			}
		
		bool operator== (const dng_rect &rect) const;
		
		bool operator!= (const dng_rect &rect) const
			{
			return !(*this == rect);
			}
			
		bool IsZero () const;
			
		bool NotZero () const
			{
			return !IsZero ();
			}
			
		bool IsEmpty () const
			{
			return (t >= b) || (l >= r);
			}
			
		bool NotEmpty () const
			{
			return !IsEmpty ();
			}
			
		// Returns the width of the rectangle, or 0 if r is smaller than l.
		// Throws an exception if the width is too large to be represented as
		// a _signed_ int32 (even if it would fit in a uint32). This is
		// consciously conservative -- there are existing uses of W() where
		// the result is converted to an int32 without an overflow check, and
		// we want to make sure no overflow can occur in such cases. We provide
		// this check in addition to the check performed in the "two-corners"
		// constructor to protect client code that produes a dng_rect with
		// excessive size by initializing or modifying the member variables
		// directly.
		uint32 W () const
			{
			if (r >= l)
				{
				int32 width;
				if (!SafeInt32Sub(r, l, &width))
					{
					ThrowProgramError ("Overflow computing rectangle width");
					}
				return static_cast<uint32>(width);
				}
			else
				{
				return 0;
				}
			}
	
		// Returns the height of the rectangle, or 0 if b is smaller than t.
		// Throws an exception if the height is too large to be represented as
		// a _signed_ int32 (see W() for rationale).
		uint32 H () const
			{
			if (b >= t)
				{
				int32 height;
				if (!SafeInt32Sub(b, t, &height))
					{
					ThrowProgramError ("Overflow computing rectangle height");
					}
				return static_cast<uint32>(height);
				}
			else
				{
				return 0;
				}
			}
		
		dng_point TL () const
			{
			return dng_point (t, l);
			}
			
		dng_point TR () const
			{
			return dng_point (t, r);
			}
			
		dng_point BL () const
			{
			return dng_point (b, l);
			}
			
		dng_point BR () const
			{
			return dng_point (b, r);
			}
			
		dng_point Size () const
			{
			return dng_point ((int32) H (), (int32) W ());
			}

		real64 Diagonal () const
			{
			return hypot ((real64) W (),
						  (real64) H ());
			}
	
	};

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

class dng_rect_real64
	{
	
	public:
	
		real64 t;
		real64 l;
		real64 b;
		real64 r;
		
	public:
		
		dng_rect_real64 ()
			:	t (0.0)
			,	l (0.0)
			,	b (0.0)
			,	r (0.0)
			{
			}
			
		dng_rect_real64 (real64 tt, real64 ll, real64 bb, real64 rr)
			:	t (tt)
			,	l (ll)
			,	b (bb)
			,	r (rr)
			{
			}
			
		dng_rect_real64 (real64 h, real64 w)
			:	t (0)
			,	l (0)
			,	b (h)
			,	r (w)
			{
			}
			
		dng_rect_real64 (const dng_point_real64 &size)
			:	t (0)
			,	l (0)
			,	b (size.v)
			,	r (size.h)
			{
			}
			
		dng_rect_real64 (const dng_point_real64 &pt1,
						 const dng_point_real64 &pt2)
			:	t (Min_real64 (pt1.v, pt2.v))
			,	l (Min_real64 (pt1.h, pt2.h))
			,	b (Max_real64 (pt1.v, pt2.v))
			,	r (Max_real64 (pt1.h, pt2.h))
			{
			}
			
		dng_rect_real64 (const dng_rect &rect)
			:	t ((real64) rect.t)
			,	l ((real64) rect.l)
			,	b ((real64) rect.b)
			,	r ((real64) rect.r)
			{
			}
		
		void Clear ()
			{
			*this = dng_point_real64 ();
			}
		
		bool operator== (const dng_rect_real64 &rect) const;
		
		bool operator!= (const dng_rect_real64 &rect) const
			{
			return !(*this == rect);
			}
			
		bool IsZero () const;
			
		bool NotZero () const
			{
			return !IsZero ();
			}
			
		bool IsEmpty () const
			{
			return (t >= b) || (l >= r);
			}
			
		bool NotEmpty () const
			{
			return !IsEmpty ();
			}
			
		real64 W () const
			{
			return Max_real64 (r - l, 0.0);
			}
	
		real64 H () const
			{
			return Max_real64 (b - t, 0.0);
			}
		
		dng_point_real64 TL () const
			{
			return dng_point_real64 (t, l);
			}
			
		dng_point_real64 TR () const
			{
			return dng_point_real64 (t, r);
			}
			
		dng_point_real64 BL () const
			{
			return dng_point_real64 (b, l);
			}
			
		dng_point_real64 BR () const
			{
			return dng_point_real64 (b, r);
			}
			
		dng_point_real64 Size () const
			{
			return dng_point_real64 (H (), W ());
			}
			
		dng_rect Round () const
			{
			return dng_rect (Round_int32 (t),
							 Round_int32 (l),
							 Round_int32 (b),
							 Round_int32 (r));
			}
	
		real64 Diagonal () const
			{
			return hypot (W (), H ());
			}
	
	};

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

dng_rect operator& (const dng_rect &a,
					const dng_rect &b);

dng_rect operator| (const dng_rect &a,
					const dng_rect &b);

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

dng_rect_real64 operator& (const dng_rect_real64 &a,
						   const dng_rect_real64 &b);

dng_rect_real64 operator| (const dng_rect_real64 &a,
						   const dng_rect_real64 &b);

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

inline dng_rect operator+ (const dng_rect &a,
					       const dng_point &b)
	{
	
	return dng_rect (a.t + b.v,
					 a.l + b.h,
					 a.b + b.v,
					 a.r + b.h);
	
	}

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

inline dng_rect_real64 operator+ (const dng_rect_real64 &a,
					       		  const dng_point_real64 &b)
	{
	
	return dng_rect_real64 (a.t + b.v,
					 		a.l + b.h,
					 		a.b + b.v,
					 		a.r + b.h);
	
	}

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

inline dng_rect operator- (const dng_rect &a,
					       const dng_point &b)
	{
	
	return dng_rect (a.t - b.v,
					 a.l - b.h,
					 a.b - b.v,
					 a.r - b.h);
	
	}

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

inline dng_rect_real64 operator- (const dng_rect_real64 &a,
					       		  const dng_point_real64 &b)
	{
	
	return dng_rect_real64 (a.t - b.v,
					 		a.l - b.h,
					 		a.b - b.v,
					 		a.r - b.h);
	
	}

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

inline dng_rect Transpose (const dng_rect &a)
	{
	
	return dng_rect (a.l, a.t, a.r, a.b);
	
	}

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

inline dng_rect_real64 Transpose (const dng_rect_real64 &a)
	{
	
	return dng_rect_real64 (a.l, a.t, a.r, a.b);
	
	}

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

inline void HalfRect (dng_rect &rect)
	{

	rect.r = rect.l + (int32) (rect.W () >> 1);
	rect.b = rect.t + (int32) (rect.H () >> 1);

	}

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

inline void DoubleRect (dng_rect &rect)
	{

	rect.r = rect.l + (int32) (rect.W () << 1);
	rect.b = rect.t + (int32) (rect.H () << 1);

	}

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

inline void InnerPadRect (dng_rect &rect,
						  int32 pad)
	{

	rect.l += pad;
	rect.r -= pad;
	rect.t += pad;
	rect.b -= pad;

	}

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

inline void OuterPadRect (dng_rect &rect,
						  int32 pad)
	{

	InnerPadRect (rect, -pad);

	}

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

inline void InnerPadRectH (dng_rect &rect,
						   int32 pad)
	{

	rect.l += pad;
	rect.r -= pad;

	}

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

inline void InnerPadRectV (dng_rect &rect,
						   int32 pad)
	{

	rect.t += pad;
	rect.b -= pad;

	}

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

inline dng_rect MakeHalfRect (const dng_rect &rect)
	{
	
	dng_rect out = rect;

	HalfRect (out);

	return out;
	
	}

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

inline dng_rect MakeDoubleRect (const dng_rect &rect)
	{
	
	dng_rect out = rect;

	DoubleRect (out);

	return out;
	
	}

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

inline dng_rect MakeInnerPadRect (const dng_rect &rect,
								  int32 pad)
	{
	
	dng_rect out = rect;

	InnerPadRect (out, pad);

	return out;
	
	}

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

inline dng_rect MakeOuterPadRect (const dng_rect &rect,
								  int32 pad)
	{
	
	dng_rect out = rect;

	OuterPadRect (out, pad);

	return out;
	
	}

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

#endif
	
/*****************************************************************************/