/*****************************************************************************/
// Copyright 2008-2019 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.
/*****************************************************************************/

/** \file
 * Opcode to fix 2D uniformity defects, such as shading.
 */

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

#ifndef __dng_gain_map__
#define __dng_gain_map__

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

#include "dng_classes.h"
#include "dng_fingerprint.h"
#include "dng_memory.h"
#include "dng_misc_opcodes.h"
#include "dng_tag_types.h"
#include "dng_uncopyable.h"

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

/// \brief Holds a discrete (i.e., sampled) 2D representation of a gain map. This is
/// effectively an image containing scale factors. 

class dng_gain_map: private dng_uncopyable
	{
	
	private:
	
		dng_point fPoints;
		
		dng_point_real64 fSpacing;
		
		dng_point_real64 fOrigin;
		
		uint32 fPlanes;
		
		uint32 fRowStep;
		
		AutoPtr<dng_memory_block> fBuffer;
		
	public:
	
		/// Construct a gain map with the specified memory allocator, number of
		/// samples (points), sample spacing, origin, and number of color planes.

		dng_gain_map (dng_memory_allocator &allocator,
					  const dng_point &points,
					  const dng_point_real64 &spacing,
					  const dng_point_real64 &origin,
					  uint32 planes);

		/// The number of samples in the horizontal and vertical directions.

		const dng_point & Points () const
			{
			return fPoints;
			}
			
		/// The space between adjacent samples in the horizontal and vertical
		/// directions.

		const dng_point_real64 & Spacing () const
			{
			return fSpacing;
			}
			
		/// The 2D coordinate for the first (i.e., top-left-most) sample.

		const dng_point_real64 & Origin () const
			{
			return fOrigin;
			}
			
		/// The number of color planes.

		uint32 Planes () const
			{
			return fPlanes;
			}

		/// Getter for a gain map sample (specified by row, column, and plane).

		real32 & Entry (uint32 rowIndex,
						uint32 colIndex,
						uint32 plane)
			{
			
			return *(fBuffer->Buffer_real32 () +
					 rowIndex * fRowStep +
					 colIndex * fPlanes	 +
					 plane);
			
			}
			
		/// Getter for a gain map sample (specified by row index, column index, and
		/// plane index).

		const real32 & Entry (uint32 rowIndex,
							  uint32 colIndex,
							  uint32 plane) const
			{
			
			return *(fBuffer->Buffer_real32 () +
					 rowIndex * fRowStep +
					 colIndex * fPlanes	 +
					 plane);
			
			}
			
		/// Compute the interpolated gain (i.e., scale factor) at the specified pixel
		/// position and color plane, within the specified image bounds (in pixels).

		real32 Interpolate (int32 row,
							int32 col,
							uint32 plane,
							const dng_rect &bounds) const;
							
		/// The number of bytes needed to hold the gain map data.

		uint32 PutStreamSize () const;
		
		/// Write the gain map to the specified stream.

		void PutStream (dng_stream &stream) const;
		
		/// Read a gain map from the specified stream.

		static dng_gain_map * GetStream (dng_host &host,
										 dng_stream &stream);

	};

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

/// \brief Holds a discrete (i.e., sampled) 2D representation of a gain table
/// map. This is effectively an image containing tables of scale factors.
/// Corresponds to the ProfileGainTableMap tag introduced in DNG 1.6 and the
/// ProfileGainTableMap2 tag introduced in DNG 1.7.

class dng_gain_table_map: private dng_uncopyable
	{
	
	private:
	
		dng_point fPoints;					 // MapPointsV, MapPointsH
		
		dng_point_real64 fSpacing;			 // MapSpacingV, MapSpacingH
		
		dng_point_real64 fOrigin;			 // MapOriginV, MapOriginH

		uint32 fNumTablePoints = 0;			 // MapPointsN
		
		uint32 fRowStep = 0;
		uint32 fColStep = 0;

		uint32 fNumSamples = 0;

		uint32 fSampleBytes = 0;

		real32 fMapInputWeights [5];		 // MapInputWeights
		
		AutoPtr<dng_memory_block> fBuffer;

		mutable dng_fingerprint fFingerprint;

		// Fields to support ProfileGainTableMap2.

		// Data type of gain values. Supported values:
		//
		// 0 = 8-bit unsigned integer
		// 1 = 16-bit unsigned integer
		// 2 = 16-bit floating-point
		// 3 = 32-bit floating-point

		uint32 fDataType = 3;

		// Gamma value.

		real32 fGamma = 1.0f;

		// Minimum and maximum gain values when data type is integer.

		real32 fGainMin = 1.0f;
		real32 fGainMax = 1.0f;

		// Buffer used to hold a copy of the original table data; used only in
		// the case of integer data types.

		AutoPtr<dng_memory_block> fOriginalBuffer;

	public:
	
		/// Construct a gain map with the specified memory allocator, number
		/// of samples (points), sample spacing, origin, number of table
		/// points, and weights.

		dng_gain_table_map (dng_memory_allocator &allocator,
							const dng_point &points,
							const dng_point_real64 &spacing,
							const dng_point_real64 &origin,
							uint32 numTablePoints,
							const real32 weights [5],
							uint32 dataType = 2,
							real32 gamma = 1.0f,
							real32 gainMin = 1.0f,
							real32 gainMax = 1.0f);

		/// The number of samples in the horizontal and vertical directions.

		const dng_point & Points () const
			{
			return fPoints;
			}
			
		/// The space between adjacent samples in the horizontal and vertical
		/// directions.

		const dng_point_real64 & Spacing () const
			{
			return fSpacing;
			}
			
		/// The 2D coordinate for the first (i.e., top-left-most) sample.

		const dng_point_real64 & Origin () const
			{
			return fOrigin;
			}

		/// Getter for the number of table points.

		uint32 NumTablePoints () const
			{
			return fNumTablePoints;
			}

		/// Getter for total number of samples. Product of table points and
		/// NumTablePoints.

		uint32 NumSamples () const
			{
			return fNumSamples;
			}
		
		/// Getter for number of bytes used to represent just the samples.

		uint32 SampleBytes () const
			{
			return fSampleBytes;
			}
		
		/// Getter for MapInputWeights.

		const real32 * MapInputWeights () const
			{
			return fMapInputWeights;
			}
			
		/// Getter for a gain table map sample (specified by row, column, and
		/// table index).

		real32 & Entry (uint32 rowIndex,
						uint32 colIndex,
						uint32 tableIndex)
			{
			
			return *(fBuffer->Buffer_real32 () +
					 rowIndex * fRowStep +
					 colIndex * fColStep +
					 tableIndex);
			
			}
			
		/// Getter for a gain map sample (specified by row index, column index, and
		/// plane index).

		const real32 & Entry (uint32 rowIndex,
							  uint32 colIndex,
							  uint32 tableIndex) const
			{
			
			return *(fBuffer->Buffer_real32 () +
					 rowIndex * fRowStep +
					 colIndex * fColStep +
					 tableIndex);
			
			}

		/// Getters for low level processing.

		uint32 RowStep () const
			{
			return fRowStep;
			}
							
		uint32 ColStep () const
			{
			return fColStep;
			}

		/// Memory block holding the fp32 version of the table.

		dng_memory_block * Block ()
			{
			return fBuffer.Get ();
			}
							
		const dng_memory_block * Block () const
			{
			return fBuffer.Get ();
			}

		/// Direct access to the fp32 version of the table.

		void * RawTablePtr () const;

		uint32 RawTableNumBytes () const;

		/// The number of bytes needed to hold the gain table map data.

		uint32 PutStreamSize () const;
		
		/// Write the gain table map to the specified stream.

		void PutStream (dng_stream &stream,
						bool forceVersion2 = false) const;

		/// Add the gain table map to the given digest printer.

		void AddDigest (dng_md5_printer &printer) const;

		/// Fingerprint for the gain table map. Computed lazily.

		dng_fingerprint GetFingerprint () const;

		/// Read a gain table map from the specified stream.

		static dng_gain_table_map * GetStream (dng_host &host,
											   dng_stream &stream,
											   bool useVersion2 = false);

		/// APIs to support ProfileGainTableMap2.

		uint32 DataType () const
			{
			return fDataType;
			}

		bool IsFloat16 () const
			{
			return fDataType == 2;
			}

		bool IsFloat32 () const
			{
			return fDataType >= 3;
			}

		bool IsUint8 () const
			{
			return fDataType == 0;
			}

		bool IsUint16 () const
			{
			return fDataType == 1;
			}

		real32 GainMin () const
			{
			return fGainMin;
			}

		real32 GainMax () const
			{
			return fGainMax;
			}

		real32 Gamma () const
			{
			return fGamma;
			}
		
		bool SupportsVersion1 () const;
		
		bool RequiresVersion2 () const
			{
			return !SupportsVersion1 ();
			}

		uint32 BytesPerEntry () const
			{
			if (fDataType == 0) return 1;	 // u8
			if (fDataType <= 2) return 2;	 // u16, fp16
			return 4;						 // fp32
			}

		// Bytes required to store the table data itself (not including
		// headers).

		uint32 DataStorageBytes () const;

		void ClearOriginalBuffer ();

		bool HasOriginalBuffer () const;

		const dng_memory_block * OriginalBuffer () const;

		void SetOriginalBuffer (AutoPtr<dng_memory_block> &blockToRelease);

	private:

		/// Ensure fingerprint has been calculated.

		void EnsureFingerprint () const;

	};

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

/// \brief An opcode to fix 2D spatially-varying light falloff or color casts (i.e.,
/// uniformity issues). This is commonly due to shading.

class dng_opcode_GainMap: public dng_inplace_opcode,
						  private dng_uncopyable
	{
	
	private:
	
		dng_area_spec fAreaSpec;
	
		AutoPtr<dng_gain_map> fGainMap;
	
	public:
	
		/// Construct a GainMap opcode for the specified image area and the specified
		/// gain map.

		dng_opcode_GainMap (const dng_area_spec &areaSpec,
							AutoPtr<dng_gain_map> &gainMap);
	
		/// Construct a GainMap opcode from the specified stream.

		dng_opcode_GainMap (dng_host &host,
							dng_stream &stream);

		/// Const accessors.

		const dng_area_spec& AreaSpec() const { return fAreaSpec; }
		const dng_gain_map& GainMap() const { return *fGainMap; }
	
		/// Write the opcode to the specified stream.

		virtual void PutData (dng_stream &stream) const override;
		
		/// The pixel data type of this opcode.

		virtual uint32 BufferPixelType (uint32 /* imagePixelType */) override
			{
			return ttFloat;
			}
	
		/// The adjusted bounds (processing area) of this opcode. It is limited to
		/// the intersection of the specified image area and the GainMap area.

		virtual dng_rect ModifiedBounds (const dng_rect &imageBounds) override
			{
			return fAreaSpec.ScaledOverlap (imageBounds);
			}
	
		/// Apply the gain map.

		virtual void ProcessArea (dng_negative &negative,
								  uint32 threadIndex,
								  dng_pixel_buffer &buffer,
								  const dng_rect &dstArea,
								  const dng_rect &imageBounds) override;

		void ApplyAreaScale (const dng_urational &scale) override
			{
			fAreaSpec.ApplyAreaScale (scale);
			}
		
	};
	
/*****************************************************************************/

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