/*****************************************************************************/
// 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
 * Opcodes to fix lens aberrations such as geometric distortion, lateral chromatic
 * aberration, and vignetting (peripheral illumination falloff).
 */

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

#ifndef __dng_lens_correction__
#define __dng_lens_correction__

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

#include "dng_1d_function.h"
#include "dng_matrix.h"
#include "dng_memory.h"
#include "dng_opcodes.h"
#include "dng_pixel_buffer.h"
#include "dng_point.h"
#include "dng_resample.h"
#include "dng_sdk_limits.h"

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

/// \brief Abstract base class holding common warp opcode parameters (e.g.,
/// number of planes, optical center) and common warp routines.

class dng_warp_params
    {

    public:

        // Number of planes to be warped. Must be either 1 or equal to the
        // number of planes of the image to be processed. If set to 1, then a
        // single set of warp parameters applies to all planes of the image.
        // fPlanes must be at least 1 and no greater than kMaxColorPlanes (see
        // dng_sdk_limits.h).

        uint32 fPlanes;

        // The optical center of the lens in normalized [0,1] coordinates with
        // respect to the image's active area. For example, a value of (0.5,
        // 0.5) indicates that the optical center of the lens is at the center
        // of the image's active area. A normalized radius of 1.0 corresponds to
        // the distance from fCenter to the farthest corner of the image's
        // active area. Each component of fCenter must lie in the range [0,1].

        dng_point_real64 fCenter;

    public:

        /// Create empty (invalid) warp parameters.

        dng_warp_params ();

        /// Create warp parameters with specified number of planes and image
        /// center.
        ///
        /// \param planes The number of planes of parameters specified: It must
        /// be either 1 or equal to the number of planes of the image to be
        /// processed.
        ///
        /// \param fCenter The image center in relative coordinates.

        dng_warp_params (uint32 planes,
                         const dng_point_real64 &fCenter);

        virtual ~dng_warp_params ();

        /// Is the entire correction a NOP for all planes?

        virtual bool IsNOPAll () const;

        /// Is the entire correction a NOP for the specified plane?

        virtual bool IsNOP (uint32 plane) const;

        /// Is the radial correction a NOP for all planes?

        virtual bool IsRadNOPAll () const;

        /// Is the radial correction a NOP for the specified plane?

        virtual bool IsRadNOP (uint32 plane) const;

        /// Is the tangential correction a NOP for all planes?

        virtual bool IsTanNOPAll () const;

        /// Is the tangential correction a NOP for the specified plane?

        virtual bool IsTanNOP (uint32 plane) const;

        /// Do these warp params appear valid?

        virtual bool IsValid () const;

        /// Are these warp params valid for the specified negative?

        virtual bool IsValidForNegative (const dng_negative &negative) const;

        /// Propagate warp parameters from first plane to all other planes.

        virtual void PropagateToAllPlanes (uint32 totalPlanes) = 0;

        /// Evaluate the 1D radial warp function for the specified plane.
        /// Parameter r is the destination (i.e., corrected) normalized radius,
        /// i.e., the normalized Euclidean distance between a corrected pixel
        /// position and the optical center in the image. r lies in the range
        /// [0,1]. The returned result is non-negative.

        virtual real64 Evaluate (uint32 plane,
                                 real64 r) const = 0;

        /// Compute and return the inverse of Evaluate () above. The base
        /// implementation uses Newton's method to perform the inversion.
        /// Parameter r is the source (i.e., uncorrected) normalized radius,
        /// i.e., normalized Euclidean distance between a corrected pixel
        /// position and the optical center in the image. Both r and the
        /// computed result are non-negative.

        virtual real64 EvaluateInverse (uint32 plane,
                                        real64 r) const;

        /// Evaluate the 1D radial warp ratio function for the specified plane.
        /// Parameter r2 is the square of the destination (i.e., corrected)
        /// normalized radius, i.e., the square of the normalized Euclidean
        /// distance between a corrected pixel position and the optical center
        /// in the image. r2 must lie in the range [0,1]. Note that this is
        /// different than the Evaluate () function, above, in that the argument
        /// to EvaluateRatio () is the square of the radius, not the radius
        /// itself. The returned result is non-negative. Mathematically,
        /// EvaluateRatio (r * r) is the same as Evaluate (r) / r.

        virtual real64 EvaluateRatio (uint32 plane,
                                      real64 r2) const = 0;

        /// Evaluate the 2D tangential warp for the specified plane. Parameter
        /// r2 is the square of the destination (i.e., corrected) normalized
        /// radius, i.e., the square of the normalized Euclidean distance
        /// between a corrected pixel position P and the optical center in the
        /// image. r2 must lie in the range [0,1]. diff contains the vertical
        /// and horizontal Euclidean distances (in pixels) between P and the
        /// optical center. diff2 contains the squares of the vertical and
        /// horizontal Euclidean distances (in pixels) between P and the optical
        /// center. The returned result is the tangential warp offset, measured
        /// in pixels.

        virtual dng_point_real64 EvaluateTangential (uint32 plane,
                                                     real64 r2,
                                                     const dng_point_real64 &diff,
                                                     const dng_point_real64 &diff2) const = 0;

        /// Evaluate the 2D tangential warp for the specified plane. diff
        /// contains the vertical and horizontal Euclidean distances (in pixels)
        /// between the destination (i.e., corrected) pixel position and the
        /// optical center in the image. The returned result is the tangential
        /// warp offset, measured in pixels.

        dng_point_real64 EvaluateTangential2 (uint32 plane,
                                              const dng_point_real64 &diff) const;

        /// Evaluate the 2D tangential warp for the specified plane. Parameter
        /// r2 is the square of the destination (i.e., corrected) normalized
        /// radius, i.e., the square of the normalized Euclidean distance
        /// between a corrected pixel position P and the optical center in the
        /// image. r2 must lie in the range [0,1]. diff contains the vertical
        /// and horizontal Euclidean distances (in pixels) between P and the
        /// optical center. The returned result is the tangential warp offset,
        /// measured in pixels.

        dng_point_real64 EvaluateTangential3 (uint32 plane,
                                              real64 r2,
                                              const dng_point_real64 &diff) const;

        /// Compute and return the maximum warped radius gap. Let D be a
        /// rectangle in a destination (corrected) image. Let rDstFar and
        /// rDstNear be the farthest and nearest points to the image center,
        /// respectively. Then the specified parameter maxDstGap is the
        /// Euclidean distance between rDstFar and rDstNear. Warp D through this
        /// warp function to a closed and bounded (generally not rectangular)
        /// region S. Let rSrcfar and rSrcNear be the farthest and nearest
        /// points to the image center, respectively. This routine returns a
        /// value that is at least (rSrcFar - rSrcNear).

        virtual real64 MaxSrcRadiusGap (real64 maxDstGap) const = 0;

        /// Compute and return the maximum warped tangential gap. minDst is the
        /// top-left pixel of the image in normalized pixel coordinates. maxDst
        /// is the bottom-right pixel of the image in normalized pixel
        /// coordinates. MaxSrcTanGap () computes the maximum absolute shift in
        /// normalized pixels in the horizontal and vertical directions that can
        /// occur as a result of the tangential warp.

        virtual dng_point_real64 MaxSrcTanGap (dng_point_real64 minDst,
                                               dng_point_real64 maxDst) const = 0;

        /// Compute and return the minimum src/dst ratio that should be used
        /// for this warp.

        virtual real64 SafeMinRatio () const = 0;

        /// Compute and return the maximum src/dst ratio that should be used
        /// for this warp.

        virtual real64 SafeMaxRatio () const = 0;

        /// Debug parameters.

        virtual void Dump () const;

    };

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

class dng_warp_params_radial
    {

    public:

        // Let N be kMaxTerms. The radial polynomial model is
        //
        // f(r) = sum_{i=0 to N-1} (k_i * r^i)
        //
        //      = k_0 + (k_1 * r) + (k_2 * r^2) + ... (k_{N-1} * r^{N-1})
        //
        // where r is the normalized ideal radius in [0,1].
        //
        // If fUseReciprocal is false, then the mapping from ideal to real
        // radius is:
        //
        //   r_real = r_ideal * f(r_ideal)
        //
        // If fUseReciprocal is true, then the mapping from ideal to real
        // radius is:
        //
        //   r_real = r_ideal / f(r_ideal)

        static const uint32 kMaxTerms = 15;

        real64 fData [kMaxColorPlanes] [kMaxTerms];

        // Index 0: min_valid_radius
        // Index 1: max_valid_radius

        real64 fValidRange [kMaxColorPlanes] [2];

        bool fUseReciprocal = false;

    public:

        dng_warp_params_radial ();

        bool IsValid (uint32 plane) const;

        bool IsNOP (uint32 plane) const;

        void SetNOP ();

        void SetNOP (uint32 plane);

        // Convenience method to set DNG 1.3 WarpRectilinear opcode radial
        // parameters.

        void SetWarpRectilinear_1_3 (uint32 plane,
                                     const dng_vector &params);

        // Returns true if the radial parameters are compatible with the DNG
        // 1.3 WarpRectilinear opcode model.

        bool CompatibleWithWarpRectilinear_1_3 (uint32 plane) const;

        // Evaluate the ideal (dst, corrected) to real (src, uncorrected)
        // radial warp model for the specified plane. r is the normalized
        // ideal image radius in the domain [0,1], where 0 is the image center
        // and 1 is the farthest image corner. Results are undefined outside
        // that domain.

        real64 Evaluate (uint32 plane,
                         real64 r) const;

        real64 EvaluateRatio (uint32 plane,
                              real64 r2) const;

    };

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

/// \brief Warp parameters for pinhole perspective rectilinear (not fisheye)
/// camera model. Supports radial and tangential (decentering) distortion
/// correction parameters.
///
/// Note the restrictions described below.

class dng_warp_params_rectilinear: public dng_warp_params
    {

    public:

        // Radial and tangential polynomial coefficients. These define a warp
        // from corrected pixel coordinates (xDst, yDst) to uncorrected pixel
        // coordinates (xSrc, ySrc) for each plane P as follows:
        //
        // Let kt0 = fTanParams [P][0]
        //     kt1 = fTanParams [P][1]
        //
        // Let (xCenter, yCenter) be the optical image center (see fCenter,
        // below) expressed in pixel coordinates. Let maxDist be the Euclidean
        // distance (in pixels) from (xCenter, yCenter) to the farthest image
        // corner.
        //
        // First, compute the normalized distance of the corrected pixel
        // position (xDst, yDst) from the image center:
        //
        //     dx = (xDst - xCenter) / maxDist
        //     dy = (yDst - yCenter) / maxDist
        //
        //     r^2 = dx^2 + dy^2
        //
        // Compute the radial correction term f(r) per dng_warp_params_radial
        // (see above) and apply as follows:
        //
        //     dxRad = dx * f(r)
        //     dyRad = dy * f(r)
        //
        // Compute the tangential correction term:
        //
        //     dxTan = (2 * kt0 * dx * dy) + kt1 * (r^2 + 2 * dx^2)
        //     dyTan = (2 * kt1 * dx * dy) + kt0 * (r^2 + 2 * dy^2)
        //
        // Compute the uncorrected pixel position (xSrc, ySrc):
        //
        //     xSrc = xCenter + (dxRad + dxTan) * maxDist
        //     ySrc = yCenter + (dyRad + dyTan) * maxDist
        //
        // Mathematical definitions and restrictions:
        //
        // Let { xSrc, ySrc } = f (xDst, yDst) be the warp function defined
        // above.
        //
        // Let xSrc = fx (xDst, yDst) be the x-component of the warp function.
        // Let ySrc = fy (xDst, yDst) be the y-component of the warp function.
        //
        // f (x, y) must be an invertible function.
        //
        // fx (x, y) must be an increasing function of x.
        // fy (x, y) must be an increasing function of x.

        dng_warp_params_radial fRadParams;

        dng_vector fTanParams [kMaxColorPlanes];

    public:

        /// Create empty (invalid) rectilinear warp parameters.

        dng_warp_params_rectilinear ();

        /// Create rectilinear warp parameters with the specified number of
        /// planes, radial component terms, tangential component terms, and
        /// image center in relative coordinates.

        dng_warp_params_rectilinear (uint32 planes,
                                     const dng_warp_params_radial &radParams,
                                     const dng_vector tanParams [],
                                     const dng_point_real64 &fCenter);

        virtual ~dng_warp_params_rectilinear ();

        // Overridden methods.

        virtual bool IsRadNOP (uint32 plane) const;

        virtual bool IsTanNOP (uint32 plane) const;

        virtual bool IsValid () const;

        virtual void PropagateToAllPlanes (uint32 totalPlanes);

        virtual real64 Evaluate (uint32 plane,
                                 real64 r) const;

        virtual real64 EvaluateRatio (uint32 plane,
                                      real64 r2) const;

        virtual dng_point_real64 EvaluateTangential (uint32 plane,
                                                     real64 r2,
                                                     const dng_point_real64 &diff,
                                                     const dng_point_real64 &diff2) const;

        virtual real64 MaxSrcRadiusGap (real64 maxDstGap) const;

        virtual dng_point_real64 MaxSrcTanGap (dng_point_real64 minDst,
                                               dng_point_real64 maxDst) const;

        virtual real64 SafeMinRatio () const;

        virtual real64 SafeMaxRatio () const;

        virtual void Dump () const;

    };

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

/// \brief Warp parameters for fisheye camera model (radial component only).
/// Note the restrictions described below.

class dng_warp_params_fisheye: public dng_warp_params
    {

    public:

        // Radial warp coefficients. These define a warp from corrected pixel
        // coordinates (xDst, yDst) to uncorrected pixel coordinates (xSrc,
        // ySrc) for each plane P as follows:
        //
        // Let kr0 = fRadParams [P][0]
        //     kr1 = fRadParams [P][1]
        //     kr2 = fRadParams [P][2]
        //     kr3 = fRadParams [P][3]
        //
        // Let (xCenter, yCenter) be the optical image center (see fCenter,
        // below) expressed in pixel coordinates. Let maxDist be the Euclidean
        // distance (in pixels) from (xCenter, yCenter) to the farthest image
        // corner.
        //
        // First, compute the normalized distance of the corrected pixel
        // position (xDst, yDst) from the image center:
        //
        //     dx = (xDst - xCenter) / maxDist
        //     dy = (yDst - yCenter) / maxDist
        //
        //     r = sqrt (dx^2 + dy^2)
        //
        // Compute the radial correction term:
        //
        //     t = atan (r)
        //
        //     rWarp = (kr0 * t) + (kr1 * t^3) + (kr2 * t^5) + (kr3 * t^7)
        //
        //     ratio = rWarp / r
        //
        //     dxRad = dx * ratio
        //     dyRad = dy * ratio
        //
        // Compute the uncorrected pixel position (xSrc, ySrc):
        //
        //     xSrc = xCenter + (dxRad * maxDist)
        //     ySrc = yCenter + (dyRad * maxDist)
        //
        // The parameters kr0, kr1, kr2, and kr3 must define an increasing
        // radial warp function. Specifically, let w (r) be the radial warp
        // function:
        //
        //     t = atan (r)
        //
        //     w (r) = (kr0 * t) + (kr1 * t^3) + (kr2 * t^5) + (kr3 * t^7).
        //
        // w (r) must be an increasing function.

        dng_vector fRadParams [kMaxColorPlanes];

    public:

        /// Create empty (invalid) fisheye warp parameters.

        dng_warp_params_fisheye ();

        /// Create rectilinear warp parameters with the specified number of
        /// planes, radial component terms, and image center in relative
        /// coordinates.

        dng_warp_params_fisheye (uint32 planes,
                                 const dng_vector radParams [],
                                 const dng_point_real64 &fCenter);

        virtual ~dng_warp_params_fisheye ();

        // Overridden methods.

        virtual bool IsRadNOP (uint32 plane) const;

        virtual bool IsTanNOP (uint32 plane) const;

        virtual bool IsValid () const;

        virtual void PropagateToAllPlanes (uint32 totalPlanes);

        virtual real64 Evaluate (uint32 plane,
                                 real64 r) const;

        virtual real64 EvaluateRatio (uint32 plane,
                                      real64 r2) const;

        virtual dng_point_real64 EvaluateTangential (uint32 plane,
                                                     real64 r2,
                                                     const dng_point_real64 &diff,
                                                     const dng_point_real64 &diff2) const;

        virtual real64 MaxSrcRadiusGap (real64 maxDstGap) const;

        virtual dng_point_real64 MaxSrcTanGap (dng_point_real64 minDst,
                                               dng_point_real64 maxDst) const;

        virtual real64 SafeMinRatio () const;

        virtual real64 SafeMaxRatio () const;

        virtual void Dump () const;

    };

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

/// \brief Abstract base class for WarpRectilinear and WarpRectilinear2 opcodes.

class dng_opcode_BaseWarpRectilinear: public dng_opcode
    {

    protected:

        dng_warp_params_rectilinear fWarpParams;

    public:

        // Overridden methods.

        bool IsNOP () const override;

        bool IsValidForNegative (const dng_negative &negative) const override;

        void Apply (dng_host &host,
                    dng_negative &negative,
                    AutoPtr<dng_image> &image) override;

        // Other methods.

        bool HasDistort () const;

        bool HasLateralCA () const;

        const dng_warp_params_rectilinear & Params () const
            {
            return fWarpParams;
            }

    protected:

        // Abstract base class.

        dng_opcode_BaseWarpRectilinear (uint32 opcodeTag,
                                        uint32 minVersion,
                                        const dng_warp_params_rectilinear &params,
                                        uint32 flags);

        dng_opcode_BaseWarpRectilinear (uint32 opcodeTag,
                                        const char *name,
                                        dng_stream &stream);

    };

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

/// \brief Warp opcode for pinhole perspective (rectilinear) camera model
/// introduced in DNG 1.3.

class dng_opcode_WarpRectilinear: public dng_opcode_BaseWarpRectilinear
    {

    public:

        dng_opcode_WarpRectilinear (const dng_warp_params_rectilinear &params,
                                    uint32 flags);

        explicit dng_opcode_WarpRectilinear (dng_stream &stream);

        // Overridden methods.

        void PutData (dng_stream &stream) const override;

    protected:

        static uint32 ParamBytes (uint32 planes);

    };

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

/// \brief Warp opcode for pinhole perspective (rectilinear) camera model
/// introduced in DNG 1.6.

class dng_opcode_WarpRectilinear2: public dng_opcode_BaseWarpRectilinear
    {

    public:

        dng_opcode_WarpRectilinear2 (const dng_warp_params_rectilinear &params,
                                     uint32 flags);

        explicit dng_opcode_WarpRectilinear2 (dng_stream &stream);

        // Overridden methods.

        void PutData (dng_stream &stream) const override;

    protected:

        static uint32 ParamBytes (uint32 planes);

    };

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

/// \brief Warp opcode for fisheye camera model.

class dng_opcode_WarpFisheye: public dng_opcode
    {

    protected:

        dng_warp_params_fisheye fWarpParams;

    public:

        dng_opcode_WarpFisheye (const dng_warp_params_fisheye &params,
                                uint32 flags);

        explicit dng_opcode_WarpFisheye (dng_stream &stream);

        // Overridden methods.

        virtual bool IsNOP () const;

        virtual bool IsValidForNegative (const dng_negative &negative) const;

        virtual void PutData (dng_stream &stream) const;

        virtual void Apply (dng_host &host,
                            dng_negative &negative,
                            AutoPtr<dng_image> &image);

    protected:

        static uint32 ParamBytes (uint32 planes);

    };

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

/// \brief Radially-symmetric vignette (peripheral illuminational falloff)
/// correction parameters.

class dng_vignette_radial_params
    {

    public:

        static const uint32 kNumTerms = 5;

    public:

        // Let v be an uncorrected pixel value of a pixel p in linear space.
        //
        // Let r be the Euclidean distance between p and the optical center.
        //
        // Compute corrected pixel value v' = v * g, where g is the gain.
        //
        // Let k0 = fParams [0]
        // Let k1 = fParams [1]
        // Let k2 = fParams [2]
        // Let k3 = fParams [3]
        // Let k4 = fParams [4]
        //
        // Gain g = 1 + (k0 * r^2) + (k1 * r^4) + (k2 * r^6) + (k3 * r^8) + (k4 * r^10)

        dng_std_vector<real64> fParams;

        dng_point_real64 fCenter;

    public:

        dng_vignette_radial_params ();

        dng_vignette_radial_params (const dng_std_vector<real64> &params,
                                    const dng_point_real64 &center);

        dng_vignette_radial_params (const dng_vignette_radial_params &params);

        bool IsNOP () const;

        bool IsValid () const;

        // For debugging.

        void Dump () const;

    };

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

/// \brief Radially-symmetric lens vignette correction opcode.

class dng_opcode_FixVignetteRadial: public dng_inplace_opcode
    {

    protected:

        dng_vignette_radial_params fParams;

        uint32 fImagePlanes;

        int64 fSrcOriginH;
        int64 fSrcOriginV;

        int64 fSrcStepH;
        int64 fSrcStepV;

        uint32 fTableInputBits;
        uint32 fTableOutputBits;

        AutoPtr<dng_memory_block> fGainTable;

        AutoPtr<dng_memory_block> fMaskBuffers [kMaxMPThreads];

    public:

        dng_opcode_FixVignetteRadial (const dng_vignette_radial_params &params,
                                      uint32 flags);

        explicit dng_opcode_FixVignetteRadial (dng_stream &stream);

        const dng_vignette_radial_params & Params () const
            {
            return fParams;
            }

        virtual bool IsNOP () const;

        virtual bool IsValidForNegative (const dng_negative &) const;

        virtual void PutData (dng_stream &stream) const;

        virtual uint32 BufferPixelType (uint32 /* imagePixelType */)
            {
            return ttFloat;
            }

        virtual void Prepare (dng_negative &negative,
                              uint32 threadCount,
                              const dng_point &tileSize,
                              const dng_rect &imageBounds,
                              uint32 imagePlanes,
                              uint32 bufferPixelType,
                              dng_memory_allocator &allocator);

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

    protected:

        static uint32 ParamBytes ();

        virtual dng_vignette_radial_params MakeParamsForRender (const dng_negative &negative);

    };

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

#endif

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