/*****************************************************************************/
// Copyright 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_lens_correction.cpp#1 $ */
/* $DateTime: 2012/05/30 13:28:51 $ */
/* $Change: 832332 $ */
/* $Author: tknoll $ */
/*****************************************************************************/
#include <cfloat>
#include <limits.h>
#include "dng_1d_table.h"
#include "dng_assertions.h"
#include "dng_bottlenecks.h"
#include "dng_exceptions.h"
#include "dng_filter_task.h"
#include "dng_globals.h"
#include "dng_host.h"
#include "dng_image.h"
#include "dng_lens_correction.h"
#include "dng_negative.h"
#include "dng_safe_arithmetic.h"
#include "dng_sdk_limits.h"
#include "dng_tag_values.h"
#include "dng_utils.h"
/*****************************************************************************/
dng_warp_params::dng_warp_params ()
: fPlanes (1)
, fCenter (0.5, 0.5)
{
}
/*****************************************************************************/
dng_warp_params::dng_warp_params (uint32 planes,
const dng_point_real64 ¢er)
: fPlanes (planes)
, fCenter (center)
{
DNG_ASSERT (planes >= 1, "Too few planes." );
DNG_ASSERT (planes <= kMaxColorPlanes, "Too many planes.");
DNG_ASSERT (fCenter.h >= 0.0 && fCenter.h <= 1.0,
"Center (horizontal) out of range.");
DNG_ASSERT (fCenter.v >= 0.0 && fCenter.v <= 1.0,
"Center (vertical) out of range.");
}
/*****************************************************************************/
dng_warp_params::~dng_warp_params ()
{
}
/*****************************************************************************/
bool dng_warp_params::IsNOPAll () const
{
return IsRadNOPAll () &&
IsTanNOPAll ();
}
/*****************************************************************************/
bool dng_warp_params::IsNOP (uint32 plane) const
{
return IsRadNOP (plane) &&
IsTanNOP (plane);
}
/*****************************************************************************/
bool dng_warp_params::IsRadNOPAll () const
{
for (uint32 plane = 0; plane < fPlanes; plane++)
{
if (!IsRadNOP (plane))
{
return false;
}
}
return true;
}
/*****************************************************************************/
bool dng_warp_params::IsRadNOP (uint32 /* plane */) const
{
return false;
}
/*****************************************************************************/
bool dng_warp_params::IsTanNOPAll () const
{
for (uint32 plane = 0; plane < fPlanes; plane++)
{
if (!IsTanNOP (plane))
{
return false;
}
}
return true;
}
/*****************************************************************************/
bool dng_warp_params::IsTanNOP (uint32 /* plane */) const
{
return false;
}
/*****************************************************************************/
bool dng_warp_params::IsValid () const
{
if (fPlanes < 1 || fPlanes > kMaxColorPlanes)
{
return false;
}
if (fCenter.h < 0.0 ||
fCenter.h > 1.0 ||
fCenter.v < 0.0 ||
fCenter.v > 1.0)
{
return false;
}
return true;
}
/*****************************************************************************/
bool dng_warp_params::IsValidForNegative (const dng_negative &negative) const
{
if (!IsValid ())
{
return false;
}
if ((fPlanes != 1) &&
(fPlanes != negative.ColorChannels ()))
{
return false;
}
return true;
}
/*****************************************************************************/
real64 dng_warp_params::EvaluateInverse (uint32 plane,
real64 y) const
{
const uint32 kMaxIterations = 30;
const real64 kNearZero = 1.0e-10;
real64 x0 = 0.0;
real64 y0 = Evaluate (plane,
x0);
real64 x1 = 1.0;
real64 y1 = Evaluate (plane,
x1);
for (uint32 iteration = 0; iteration < kMaxIterations; iteration++)
{
if (Abs_real64 (y1 - y0) < kNearZero)
{
break;
}
const real64 x2 = Pin_real64 (0.0,
x1 + (y - y1) * (x1 - x0) / (y1 - y0),
1.0);
const real64 y2 = Evaluate (plane,
x2);
x0 = x1;
y0 = y1;
x1 = x2;
y1 = y2;
}
return x1;
}
/*****************************************************************************/
dng_point_real64 dng_warp_params::EvaluateTangential2 (uint32 plane,
const dng_point_real64 &diff) const
{
const real64 dvdv = diff.v * diff.v;
const real64 dhdh = diff.h * diff.h;
const real64 rr = dvdv + dhdh;
dng_point_real64 diffSqr (dvdv,
dhdh);
return EvaluateTangential (plane,
rr,
diff,
diffSqr);
}
/*****************************************************************************/
dng_point_real64 dng_warp_params::EvaluateTangential3 (uint32 plane,
real64 r2,
const dng_point_real64 &diff) const
{
dng_point_real64 diffSqr (diff.v * diff.v,
diff.h * diff.h);
return EvaluateTangential (plane,
r2,
diff,
diffSqr);
}
/*****************************************************************************/
void dng_warp_params::Dump () const
{
#if qDNGValidate
printf ("Planes: %u\n", (unsigned) fPlanes);
printf (" Optical center:\n"
" h = %.6lf\n"
" v = %.6lf\n",
fCenter.h,
fCenter.v);
#endif
}
/*****************************************************************************/
dng_warp_params_rectilinear::dng_warp_params_rectilinear ()
: dng_warp_params ()
{
for (uint32 plane = 0; plane < kMaxColorPlanes; plane++)
{
fRadParams [plane] = dng_vector (4);
fTanParams [plane] = dng_vector (2);
fRadParams [plane][0] = 1.0;
}
}
/*****************************************************************************/
dng_warp_params_rectilinear::dng_warp_params_rectilinear (uint32 planes,
const dng_vector radParams [],
const dng_vector tanParams [],
const dng_point_real64 ¢er)
: dng_warp_params (planes,
center)
{
for (uint32 i = 0; i < fPlanes; i++)
{
fRadParams [i] = radParams [i];
fTanParams [i] = tanParams [i];
}
}
/*****************************************************************************/
dng_warp_params_rectilinear::~dng_warp_params_rectilinear ()
{
}
/*****************************************************************************/
bool dng_warp_params_rectilinear::IsRadNOP (uint32 plane) const
{
DNG_ASSERT (plane < fPlanes, "plane out of range.");
const dng_vector &r = fRadParams [plane];
return (r [0] == 1.0 &&
r [1] == 0.0 &&
r [2] == 0.0 &&
r [3] == 0.0);
}
/*****************************************************************************/
bool dng_warp_params_rectilinear::IsTanNOP (uint32 plane) const
{
DNG_ASSERT (plane < fPlanes, "plane out of range.");
const dng_vector &t = fTanParams [plane];
return (t [0] == 0.0 &&
t [1] == 0.0);
}
/*****************************************************************************/
bool dng_warp_params_rectilinear::IsValid () const
{
for (uint32 plane = 0; plane < fPlanes; plane++)
{
if (fRadParams [plane].Count () != 4)
{
return false;
}
if (fTanParams [plane].Count () < 2)
{
return false;
}
}
return dng_warp_params::IsValid ();
}
/*****************************************************************************/
void dng_warp_params_rectilinear::PropagateToAllPlanes (uint32 totalPlanes)
{
for (uint32 plane = fPlanes; plane < totalPlanes; plane++)
{
fRadParams [plane] = fRadParams [0];
fTanParams [plane] = fTanParams [0];
}
}
/*****************************************************************************/
real64 dng_warp_params_rectilinear::Evaluate (uint32 plane,
real64 x) const
{
const dng_vector &K = fRadParams [plane]; // Coefficients.
const real64 x2 = x * x;
return x * (K [0] + x2 * (K [1] + x2 * (K [2] + x2 * K [3])));
}
/*****************************************************************************/
real64 dng_warp_params_rectilinear::EvaluateRatio (uint32 plane,
real64 r2) const
{
const dng_vector &K = fRadParams [plane]; // Coefficients.
return K [0] + r2 * (K [1] + r2 * (K [2] + r2 * K [3]));
}
/*****************************************************************************/
dng_point_real64 dng_warp_params_rectilinear::EvaluateTangential (uint32 plane,
real64 r2,
const dng_point_real64 &diff,
const dng_point_real64 &diff2) const
{
const real64 kt0 = fTanParams [plane][0];
const real64 kt1 = fTanParams [plane][1];
const real64 dh = diff.h;
const real64 dv = diff.v;
const real64 dhdh = diff2.h;
const real64 dvdv = diff2.v;
return dng_point_real64 (kt0 * (r2 + 2.0 * dvdv) + (2.0 * kt1 * dh * dv), // v
kt1 * (r2 + 2.0 * dhdh) + (2.0 * kt0 * dh * dv)); // h
}
/*****************************************************************************/
real64 dng_warp_params_rectilinear::MaxSrcRadiusGap (real64 maxDstGap) const
{
real64 maxSrcGap = 0.0;
for (uint32 plane = 0; plane < fPlanes; plane++)
{
const dng_vector &coefs = fRadParams [plane];
const real64 k3 = coefs [1];
const real64 k5 = coefs [2];
const real64 k7 = coefs [3];
//
// Let f (r) be the radius warp function. Consider the function
//
// gap (r) = f (r + maxDstGap) - f (r)
//
// We wish to maximize gap (r) over the domain [0, 1 - maxDstGap]. This will
// give us a reasonable upper bound on the src tile size, independent of
// dstBounds.
//
// As usual, we maximize gap (r) by examining its critical points, i.e., by
// considering the roots of its derivative which lie in the domain [0, 1 -
// maxDstGap]. gap' (r) is a 5th-order polynomial. One of its roots is
// -maxDstGap / 2, which is negative and hence lies outside the domain of
// interest. This leaves 4 other possible roots. We solve for these
// analytically below.
//
real64 roots [4];
uint32 numRoots = 0;
if (k7 == 0.0)
{
if (k5 == 0.0)
{
// No roots in [0,1].
}
else
{
// k7 is zero, but k5 is non-zero. At most two real roots.
const real64 discrim = 25.0 * (-6.0 * k3 * k5 - 5.0 * k5 * maxDstGap * maxDstGap);
if (discrim >= 0.0)
{
// Two real roots.
const real64 scale = 0.1 * k5;
const real64 offset = -5.0 * maxDstGap * k5;
const real64 sDiscrim = sqrt (discrim);
roots [numRoots++] = scale * (offset + sDiscrim);
roots [numRoots++] = scale * (offset - sDiscrim);
}
}
}
else
{
// k7 is non-zero. Up to 4 real roots.
const real64 d = maxDstGap;
const real64 d2 = d * d;
const real64 d4 = d2 * d2;
const real64 discrim = 25.0 * k5 * k5
- 63.0 * k3 * k7
+ 35.0 * d2 * k5 * k7
+ 49.0 * d4 * k7 * k7;
if (discrim >= 0.0)
{
const real64 sDiscrim = 4.0 * k7 * sqrt (discrim);
const real64 offset = -20.0 * k5 * k7 - 35.0 * d2 * k7 * k7;
const real64 discrim1 = offset - sDiscrim;
const real64 discrim2 = offset + sDiscrim;
const real64 scale = sqrt (21.0) / (42.0 * k7);
if (discrim1 >= 0.0)
{
const real64 offset1 = -d * 0.5;
const real64 sDiscrim1 = scale * sqrt (discrim1);
roots [numRoots++] = offset1 + sDiscrim1;
roots [numRoots++] = offset1 - sDiscrim1;
}
if (discrim2 >= 0.0)
{
const real64 offset2 = -d * 0.5;
const real64 sDiscrim2 = scale * sqrt (discrim2);
roots [numRoots++] = offset2 + sDiscrim2;
roots [numRoots++] = offset2 - sDiscrim2;
}
}
}
real64 planeMaxSrcGap = 0.0;
// Examine the endpoints.
{
// Check left endpoint: f (maxDstGap) - f (0). Remember that f (0) == 0.
const real64 gap1 = Evaluate (plane, maxDstGap);
planeMaxSrcGap = Max_real64 (planeMaxSrcGap, gap1);
// Check right endpoint: f (1) - f (1 - maxDstGap).
const real64 gap2 = Evaluate (plane, 1.0)
- Evaluate (plane, 1.0 - maxDstGap);
planeMaxSrcGap = Max_real64 (planeMaxSrcGap, gap2);
}
// Examine the roots we found, if any.
for (uint32 i = 0; i < numRoots; i++)
{
const real64 r = roots [i];
if (r > 0.0 && r < 1.0 - maxDstGap)
{
const real64 gap = Evaluate (plane, r + maxDstGap)
- Evaluate (plane, r);
planeMaxSrcGap = Max_real64 (planeMaxSrcGap, gap);
}
}
maxSrcGap = Max_real64 (maxSrcGap,
planeMaxSrcGap);
}
return maxSrcGap;
}
/*****************************************************************************/
dng_point_real64 dng_warp_params_rectilinear::MaxSrcTanGap (dng_point_real64 minDst,
dng_point_real64 maxDst) const
{
const real64 v [] = { minDst.v, maxDst.v, 0.0 };
const real64 h [] = { minDst.h, maxDst.h, 0.0 };
dng_point_real64 maxGap;
for (uint32 plane = 0; plane < fPlanes; plane++)
{
real64 hMin = +FLT_MAX;
real64 hMax = -FLT_MAX;
real64 vMin = +FLT_MAX;
real64 vMax = -FLT_MAX;
for (uint32 i = 0; i < 3; i++)
{
for (uint32 j = 0; j < 3; j++)
{
dng_point_real64 dstDiff (v [i],
h [j]);
dng_point_real64 srcDiff = EvaluateTangential2 (plane,
dstDiff);
hMin = Min_real64 (hMin, srcDiff.h);
hMax = Max_real64 (hMax, srcDiff.h);
vMin = Min_real64 (vMin, srcDiff.v);
vMax = Max_real64 (vMax, srcDiff.v);
}
}
const real64 hGap = hMax - hMin;
const real64 vGap = vMax - vMin;
maxGap.h = Max_real64 (maxGap.h, hGap);
maxGap.v = Max_real64 (maxGap.v, vGap);
}
return maxGap;
}
/*****************************************************************************/
void dng_warp_params_rectilinear::Dump () const
{
#if qDNGValidate
dng_warp_params::Dump ();
for (uint32 plane = 0; plane < fPlanes; plane++)
{
printf (" Plane %u:\n", (unsigned) plane);
printf (" Radial params: %.6lf, %.6lf, %.6lf, %.6lf\n",
fRadParams [plane][0],
fRadParams [plane][1],
fRadParams [plane][2],
fRadParams [plane][3]);
printf (" Tangential params: %.6lf, %.6lf\n",
fTanParams [plane][0],
fTanParams [plane][1]);
}
#endif
}
/*****************************************************************************/
dng_warp_params_fisheye::dng_warp_params_fisheye ()
: dng_warp_params ()
{
for (uint32 plane = 0; plane < kMaxColorPlanes; plane++)
{
fRadParams [plane] = dng_vector (4);
}
}
/*****************************************************************************/
dng_warp_params_fisheye::dng_warp_params_fisheye (uint32 planes,
const dng_vector radParams [],
const dng_point_real64 ¢er)
: dng_warp_params (planes, center)
{
for (uint32 i = 0; i < fPlanes; i++)
{
fRadParams [i] = radParams [i];
}
}
/*****************************************************************************/
dng_warp_params_fisheye::~dng_warp_params_fisheye ()
{
}
/*****************************************************************************/
bool dng_warp_params_fisheye::IsRadNOP (uint32 /* plane */) const
{
return false;
}
/*****************************************************************************/
bool dng_warp_params_fisheye::IsTanNOP (uint32 /* plane */) const
{
return true;
}
/*****************************************************************************/
bool dng_warp_params_fisheye::IsValid () const
{
for (uint32 plane = 0; plane < fPlanes; plane++)
{
if (fRadParams [plane].Count () != 4)
{
return false;
}
}
return dng_warp_params::IsValid ();
}
/*****************************************************************************/
void dng_warp_params_fisheye::PropagateToAllPlanes (uint32 totalPlanes)
{
for (uint32 plane = fPlanes; plane < totalPlanes; plane++)
{
fRadParams [plane] = fRadParams [0];
}
}
/*****************************************************************************/
real64 dng_warp_params_fisheye::Evaluate (uint32 plane,
real64 r) const
{
const real64 t = atan (r);
const dng_vector &K = fRadParams [plane];
const real64 t2 = t * t;
return t * (K [0] + t2 * (K [1] + t2 * (K [2] + t2 * K [3])));
}
/*****************************************************************************/
real64 dng_warp_params_fisheye::EvaluateRatio (uint32 plane,
real64 rSqr) const
{
const real64 eps = 1.0e-12;
if (rSqr < eps)
{
// r is very close to zero.
return 1.0;
}
const real64 r = sqrt (rSqr);
return Evaluate (plane, r) / r;
}
/*****************************************************************************/
dng_point_real64 dng_warp_params_fisheye::EvaluateTangential (uint32 /* plane */,
real64 /* r2 */,
const dng_point_real64 & /* diff */,
const dng_point_real64 & /* diff2 */) const
{
// This fisheye model does not support tangential warping.
ThrowProgramError ();
return dng_point_real64 (0.0, 0.0);
}
/*****************************************************************************/
real64 dng_warp_params_fisheye::MaxSrcRadiusGap (real64 maxDstGap) const
{
//
// Let f (r) be the radius warp function. Consider the function
//
// gap (r) = f (r + maxDstGap) - f (r)
//
// We wish to maximize gap (r) over the domain [0, 1 - maxDstGap]. This will
// give us a reasonable upper bound on the src tile size, independent of
// dstBounds.
//
// Ideally, we'd like to maximize gap (r) by examining its critical points,
// i.e., by considering the roots of its derivative which lie in the domain [0,
// 1 - maxDstGap]. However, gap' (r) is too complex to find its roots
// analytically.
//
real64 maxSrcGap = 0.0;
DNG_REQUIRE (maxDstGap > 0.0, "maxDstGap must be positive.");
const real64 kMaxValue = 1.0 - maxDstGap;
const uint32 kSteps = 128;
const real64 kNorm = kMaxValue / real64 (kSteps - 1);
for (uint32 plane = 0; plane < fPlanes; plane++)
{
for (uint32 i = 0; i < kSteps; i++)
{
const real64 tt = i * kNorm;
const real64 gap = Evaluate (plane, tt + maxDstGap)
- Evaluate (plane, tt);
maxSrcGap = Max_real64 (maxSrcGap,
gap);
}
}
return maxSrcGap;
}
/*****************************************************************************/
dng_point_real64 dng_warp_params_fisheye::MaxSrcTanGap (dng_point_real64 /* minDst */,
dng_point_real64 /* maxDst */) const
{
// This fisheye model does not support tangential distortion.
return dng_point_real64 (0.0, 0.0);
}
/*****************************************************************************/
void dng_warp_params_fisheye::Dump () const
{
#if qDNGValidate
dng_warp_params::Dump ();
for (uint32 plane = 0; plane < fPlanes; plane++)
{
printf (" Plane %u:\n", (unsigned) plane);
printf (" Radial params: %.6lf, %.6lf, %.6lf, %.6lf\n",
fRadParams [plane][0],
fRadParams [plane][1],
fRadParams [plane][2],
fRadParams [plane][3]);
}
#endif
}
/*****************************************************************************/
class dng_filter_warp: public dng_filter_task
{
protected:
AutoPtr<dng_warp_params> fParams;
dng_point_real64 fCenter;
dng_resample_weights_2d fWeights;
real64 fNormRadius;
real64 fInvNormRadius;
bool fIsRadNOP;
bool fIsTanNOP;
const real64 fPixelScaleV;
const real64 fPixelScaleVInv;
public:
dng_filter_warp (const dng_image &srcImage,
dng_image &dstImage,
const dng_negative &negative,
AutoPtr<dng_warp_params> ¶ms);
virtual void Initialize (dng_host &host);
virtual dng_rect SrcArea (const dng_rect &dstArea);
virtual dng_point SrcTileSize (const dng_point &dstTileSize);
virtual void ProcessArea (uint32 threadIndex,
dng_pixel_buffer &srcBuffer,
dng_pixel_buffer &dstBuffer);
virtual dng_point_real64 GetSrcPixelPosition (const dng_point_real64 &dst,
uint32 plane);
};
/*****************************************************************************/
dng_filter_warp::dng_filter_warp (const dng_image &srcImage,
dng_image &dstImage,
const dng_negative &negative,
AutoPtr<dng_warp_params> ¶ms)
: dng_filter_task (srcImage,
dstImage)
, fParams (params.Release ())
, fCenter ()
, fWeights ()
, fNormRadius (1.0)
, fInvNormRadius (1.0)
, fIsRadNOP (false)
, fIsTanNOP (false)
, fPixelScaleV (1.0 / negative.PixelAspectRatio ())
, fPixelScaleVInv (1.0 / fPixelScaleV)
{
// Force float processing.
fSrcPixelType = ttFloat;
fDstPixelType = ttFloat;
fIsRadNOP = fParams->IsRadNOPAll ();
fIsTanNOP = fParams->IsTanNOPAll ();
const uint32 negPlanes = negative.ColorChannels ();
DNG_ASSERT (negPlanes >= 1, "Too few planes." );
DNG_ASSERT (negPlanes <= kMaxColorPlanes, "Too many planes.");
(void) negPlanes;
// At least one set of params must do something interesting.
if (fIsRadNOP && fIsTanNOP)
{
ThrowProgramError ();
}
// Make sure the warp params are valid for this negative.
if (!fParams->IsValidForNegative (negative))
{
ThrowBadFormat ();
}
// Compute center.
const dng_rect bounds = srcImage.Bounds ();
fCenter.h = Lerp_real64 ((real64) bounds.l,
(real64) bounds.r,
fParams->fCenter.h);
fCenter.v = Lerp_real64 ((real64) bounds.t,
(real64) bounds.b,
fParams->fCenter.v);
// Compute max pixel radius and derive various normalized radius values. Note
// that when computing the max pixel radius, we must take into account the pixel
// aspect ratio.
{
dng_rect squareBounds (bounds);
squareBounds.b = squareBounds.t +
Round_int32 (fPixelScaleV * (real64) squareBounds.H ());
const dng_point_real64 squareCenter (Lerp_real64 ((real64) squareBounds.t,
(real64) squareBounds.b,
fParams->fCenter.v),
Lerp_real64 ((real64) squareBounds.l,
(real64) squareBounds.r,
fParams->fCenter.h));
fNormRadius = MaxDistancePointToRect (squareCenter,
squareBounds);
fInvNormRadius = 1.0 / fNormRadius;
}
// Propagate warp params to other planes.
fParams->PropagateToAllPlanes (fDstPlanes);
}
/*****************************************************************************/
void dng_filter_warp::Initialize (dng_host &host)
{
// Make resample weights.
const dng_resample_function &kernel = dng_resample_bicubic::Get ();
fWeights.Initialize (kernel,
host.Allocator ());
}
/*****************************************************************************/
dng_rect dng_filter_warp::SrcArea (const dng_rect &dstArea)
{
// Walk each pixel of the boundary of dstArea, map it to the uncorrected src
// pixel position, and return the rectangle that contains all such src pixels.
int32 xMin = INT_MAX;
int32 xMax = INT_MIN;
int32 yMin = INT_MAX;
int32 yMax = INT_MIN;
for (uint32 plane = 0; plane < fDstPlanes; plane++)
{
// Top and bottom edges.
for (int32 c = dstArea.l; c < dstArea.r; c++)
{
// Top edge.
{
const dng_point_real64 dst (dstArea.t, c);
const dng_point_real64 src = GetSrcPixelPosition (dst, plane);
const int32 y = ConvertDoubleToInt32 (floor (src.v));
yMin = Min_int32 (yMin, y);
}
// Bottom edge.
{
const dng_point_real64 dst (dstArea.b - 1, c);
const dng_point_real64 src = GetSrcPixelPosition (dst, plane);
const int32 y = ConvertDoubleToInt32 (ceil (src.v));
yMax = Max_int32 (yMax, y);
}
}
// Left and right edges.
for (int32 r = dstArea.t; r < dstArea.b; r++)
{
// Left edge.
{
const dng_point_real64 dst (r, dstArea.l);
const dng_point_real64 src = GetSrcPixelPosition (dst, plane);
const int32 x = ConvertDoubleToInt32 (floor (src.h));
xMin = Min_int32 (xMin, x);
}
// Right edge.
{
const dng_point_real64 dst (r, dstArea.r - 1);
const dng_point_real64 src = GetSrcPixelPosition (dst, plane);
const int32 x = ConvertDoubleToInt32 (ceil (src.h));
xMax = Max_int32 (xMax, x);
}
}
}
// Pad each side by filter radius.
const int32 pad = ConvertUint32ToInt32(fWeights.Radius());
xMin = SafeInt32Sub(xMin, pad);
yMin = SafeInt32Sub(yMin, pad);
xMax = SafeInt32Add(xMax, pad);
yMax = SafeInt32Add(yMax, pad);
xMax = SafeInt32Add(xMax, 1);
yMax = SafeInt32Add(yMax, 1);
const dng_rect srcArea (yMin,
xMin,
yMax,
xMax);
return srcArea;
}
/*****************************************************************************/
dng_point dng_filter_warp::SrcTileSize (const dng_point &dstTileSize)
{
// Obtain an upper bound on the source tile size. We'll do this by considering
// upper bounds on the radial and tangential warp components separately, then add
// them together. This is not a tight bound but is good enough because the
// tangential terms are usually quite small.
// Get upper bound on src tile size from radial warp.
DNG_REQUIRE (dstTileSize.v > 0, "Invalid tile height.");
DNG_REQUIRE (dstTileSize.h > 0, "Invalid tile width.");
const real64 maxDstGap = fInvNormRadius * hypot ((real64) dstTileSize.h,
(real64) dstTileSize.v);
dng_point srcTileSize;
if (maxDstGap >= 1.0)
{
// The proposed tile size is unusually large, i.e., its hypotenuse is larger
// than the maximum radius. Bite the bullet and just return a tile size big
// enough to process the whole image.
srcTileSize = SrcArea (fDstImage.Bounds ()).Size ();
}
else
{
// maxDstGap < 1.0.
const real64 maxSrcGap = fParams->MaxSrcRadiusGap (maxDstGap);
const int32 dim = ConvertDoubleToInt32 (ceil (maxSrcGap * fNormRadius));
srcTileSize = dng_point (dim, dim);
}
srcTileSize.h += ConvertUint32ToInt32(fWeights.Width());
srcTileSize.v += ConvertUint32ToInt32(fWeights.Width());
// Get upper bound on src tile size from tangential warp.
const dng_rect_real64 bounds (fSrcImage.Bounds ());
const dng_point_real64 minDst ((bounds.t - fCenter.v) * fInvNormRadius,
(bounds.l - fCenter.h) * fInvNormRadius);
const dng_point_real64 maxDst ((bounds.b - 1.0 - fCenter.v) * fInvNormRadius,
(bounds.r - 1.0 - fCenter.h) * fInvNormRadius);
const dng_point_real64 srcTanGap = fParams->MaxSrcTanGap (minDst,
maxDst);
// Add the two bounds together.
srcTileSize.v += ConvertDoubleToInt32 (ceil (srcTanGap.v * fNormRadius));
srcTileSize.h += ConvertDoubleToInt32 (ceil (srcTanGap.h * fNormRadius));
return srcTileSize;
}
/*****************************************************************************/
void dng_filter_warp::ProcessArea (uint32 /* threadIndex */,
dng_pixel_buffer &srcBuffer,
dng_pixel_buffer &dstBuffer)
{
// Prepare resample constants.
const int32 wCount = fWeights.Width ();
const dng_point srcOffset (fWeights.Offset (),
fWeights.Offset ());
const real64 numSubsamples = (real64) kResampleSubsampleCount2D;
// Prepare area and step constants.
const dng_rect srcArea = srcBuffer.fArea;
const dng_rect dstArea = dstBuffer.fArea;
const int32 srcRowStep = (int32) srcBuffer.RowStep ();
const int32 hMin = srcArea.l;
const int32 hMax = SafeInt32Sub (SafeInt32Sub (srcArea.r, wCount), 1);
const int32 vMin = srcArea.t;
const int32 vMax = SafeInt32Sub (SafeInt32Sub (srcArea.b, wCount), 1);
if (hMax < hMin || vMax < vMin)
{
ThrowBadFormat ("Empty source area in dng_filter_warp.");
}
// Warp each plane.
for (uint32 plane = 0; plane < dstBuffer.fPlanes; plane++)
{
real32 *dPtr = dstBuffer.DirtyPixel_real32 (dstArea.t,
dstArea.l,
plane);
for (int32 dstRow = dstArea.t; dstRow < dstArea.b; dstRow++)
{
uint32 dstIndex = 0;
for (int32 dstCol = dstArea.l; dstCol < dstArea.r; dstCol++, dstIndex++)
{
// Get destination (corrected) pixel position.
const dng_point_real64 dPos ((real64) dstRow,
(real64) dstCol);
// Warp to source (uncorrected) pixel position.
const dng_point_real64 sPos = GetSrcPixelPosition (dPos,
plane);
// Decompose into integer and fractional parts.
dng_point sInt (ConvertDoubleToInt32 (floor (sPos.v)),
ConvertDoubleToInt32 (floor (sPos.h)));
dng_point sFct (ConvertDoubleToInt32 ((sPos.v - (real64) sInt.v) * numSubsamples),
ConvertDoubleToInt32 ((sPos.h - (real64) sInt.h) * numSubsamples));
// Add resample offset.
sInt = sInt + srcOffset;
// Clip.
if (sInt.h < hMin)
{
sInt.h = hMin;
sFct.h = 0;
}
else if (sInt.h > hMax)
{
sInt.h = hMax;
sFct.h = 0;
}
if (sInt.v < vMin)
{
sInt.v = vMin;
sFct.v = 0;
}
else if (sInt.v > vMax)
{
sInt.v = vMax;
sFct.v = 0;
}
// Perform 2D resample.
const real32 *w = fWeights.Weights32 (sFct);
const real32 *s = srcBuffer.ConstPixel_real32 (sInt.v,
sInt.h,
plane);
real32 total = 0.0f;
for (int32 i = 0; i < wCount; i++)
{
for (int32 j = 0; j < wCount; j++)
{
total += w [j] * s [j];
}
w += wCount;
s += srcRowStep;
}
// Store final pixel value.
dPtr [dstIndex] = Pin_real32 (total);
}
// Advance to next row.
dPtr += dstBuffer.RowStep ();
}
}
}
/*****************************************************************************/
dng_point_real64 dng_filter_warp::GetSrcPixelPosition (const dng_point_real64 &dst,
uint32 plane)
{
const dng_point_real64 diff = dst - fCenter;
const dng_point_real64 diffNorm (diff.v * fInvNormRadius,
diff.h * fInvNormRadius);
const dng_point_real64 diffNormScaled (diffNorm.v * fPixelScaleV,
diffNorm.h);
const dng_point_real64 diffNormSqr (diffNormScaled.v * diffNormScaled.v,
diffNormScaled.h * diffNormScaled.h);
const real64 rr = Min_real64 (diffNormSqr.v + diffNormSqr.h,
1.0);
dng_point_real64 dSrc;
if (fIsTanNOP)
{
// Radial only.
const real64 ratio = fParams->EvaluateRatio (plane,
rr);
dSrc.h = diff.h * ratio;
dSrc.v = diff.v * ratio;
}
else if (fIsRadNOP)
{
// Tangential only.
const dng_point_real64 tan = fParams->EvaluateTangential (plane,
rr,
diffNormScaled,
diffNormSqr);
dSrc.h = diff.h + (fNormRadius * tan.h);
dSrc.v = diff.v + (fNormRadius * tan.v * fPixelScaleVInv);
}
else
{
// Radial and tangential.
const real64 ratio = fParams->EvaluateRatio (plane,
rr);
const dng_point_real64 tan = fParams->EvaluateTangential (plane,
rr,
diffNormScaled,
diffNormSqr);
dSrc.h = fNormRadius * (diffNorm.h * ratio + tan.h);
dSrc.v = fNormRadius * (diffNorm.v * ratio + tan.v * fPixelScaleVInv);
}
return fCenter + dSrc;
}
/*****************************************************************************/
dng_opcode_WarpRectilinear::dng_opcode_WarpRectilinear (const dng_warp_params_rectilinear ¶ms,
uint32 flags)
: dng_opcode (dngOpcode_WarpRectilinear,
dngVersion_1_3_0_0,
flags)
, fWarpParams (params)
{
if (!params.IsValid ())
{
ThrowBadFormat ();
}
}
/*****************************************************************************/
dng_opcode_WarpRectilinear::dng_opcode_WarpRectilinear (dng_stream &stream)
: dng_opcode (dngOpcode_WarpRectilinear,
stream,
"WarpRectilinear")
, fWarpParams ()
{
// Grab the size in bytes.
const uint32 bytes = stream.Get_uint32 ();
// Grab the number of planes to warp.
fWarpParams.fPlanes = stream.Get_uint32 ();
// Verify number of planes.
if (fWarpParams.fPlanes == 0 ||
fWarpParams.fPlanes > kMaxColorPlanes)
{
ThrowBadFormat ();
}
// Verify the size.
if (bytes != ParamBytes (fWarpParams.fPlanes))
{
ThrowBadFormat ();
}
// Read warp parameters for each plane.
for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++)
{
fWarpParams.fRadParams [plane][0] = stream.Get_real64 ();
fWarpParams.fRadParams [plane][1] = stream.Get_real64 ();
fWarpParams.fRadParams [plane][2] = stream.Get_real64 ();
fWarpParams.fRadParams [plane][3] = stream.Get_real64 ();
fWarpParams.fTanParams [plane][0] = stream.Get_real64 ();
fWarpParams.fTanParams [plane][1] = stream.Get_real64 ();
}
// Read the image center.
fWarpParams.fCenter.h = stream.Get_real64 ();
fWarpParams.fCenter.v = stream.Get_real64 ();
#if qDNGValidate
if (gVerbose)
{
fWarpParams.Dump ();
}
#endif
if (!fWarpParams.IsValid ())
{
ThrowBadFormat ();
}
}
/*****************************************************************************/
bool dng_opcode_WarpRectilinear::IsNOP () const
{
return fWarpParams.IsNOPAll ();
}
/*****************************************************************************/
bool dng_opcode_WarpRectilinear::IsValidForNegative (const dng_negative &negative) const
{
return fWarpParams.IsValidForNegative (negative);
}
/*****************************************************************************/
void dng_opcode_WarpRectilinear::PutData (dng_stream &stream) const
{
const uint32 bytes = ParamBytes (fWarpParams.fPlanes);
stream.Put_uint32 (bytes);
stream.Put_uint32 (fWarpParams.fPlanes);
for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++)
{
stream.Put_real64 (fWarpParams.fRadParams [plane][0]);
stream.Put_real64 (fWarpParams.fRadParams [plane][1]);
stream.Put_real64 (fWarpParams.fRadParams [plane][2]);
stream.Put_real64 (fWarpParams.fRadParams [plane][3]);
stream.Put_real64 (fWarpParams.fTanParams [plane][0]);
stream.Put_real64 (fWarpParams.fTanParams [plane][1]);
}
stream.Put_real64 (fWarpParams.fCenter.h);
stream.Put_real64 (fWarpParams.fCenter.v);
}
/*****************************************************************************/
void dng_opcode_WarpRectilinear::Apply (dng_host &host,
dng_negative &negative,
AutoPtr<dng_image> &image)
{
#if qDNGValidate
dng_timer timer ("WarpRectilinear time");
#endif
// Allocate destination image.
AutoPtr<dng_image> dstImage (host.Make_dng_image (image->Bounds (),
image->Planes (),
image->PixelType ()));
// Warp the image.
AutoPtr<dng_warp_params> params (new dng_warp_params_rectilinear (fWarpParams));
dng_filter_warp filter (*image,
*dstImage,
negative,
params);
filter.Initialize (host);
host.PerformAreaTask (filter,
image->Bounds ());
// Return the new image.
image.Reset (dstImage.Release ());
}
/*****************************************************************************/
uint32 dng_opcode_WarpRectilinear::ParamBytes (uint32 planes)
{
return (1 * (uint32) sizeof (uint32) ) + // Number of planes.
(6 * (uint32) sizeof (real64) * planes) + // Warp coefficients.
(2 * (uint32) sizeof (real64) ); // Optical center.
}
/*****************************************************************************/
dng_opcode_WarpFisheye::dng_opcode_WarpFisheye (const dng_warp_params_fisheye ¶ms,
uint32 flags)
: dng_opcode (dngOpcode_WarpFisheye,
dngVersion_1_3_0_0,
flags)
, fWarpParams (params)
{
if (!params.IsValid ())
{
ThrowBadFormat ();
}
}
/*****************************************************************************/
dng_opcode_WarpFisheye::dng_opcode_WarpFisheye (dng_stream &stream)
: dng_opcode (dngOpcode_WarpFisheye,
stream,
"WarpFisheye")
, fWarpParams ()
{
// Grab the size in bytes.
const uint32 bytes = stream.Get_uint32 ();
// Grab the number of planes to warp.
fWarpParams.fPlanes = stream.Get_uint32 ();
// Verify number of planes.
if (fWarpParams.fPlanes == 0 ||
fWarpParams.fPlanes > kMaxColorPlanes)
{
ThrowBadFormat ();
}
// Verify the size.
if (bytes != ParamBytes (fWarpParams.fPlanes))
{
ThrowBadFormat ();
}
// Read warp parameters for each plane.
for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++)
{
fWarpParams.fRadParams [plane][0] = stream.Get_real64 ();
fWarpParams.fRadParams [plane][1] = stream.Get_real64 ();
fWarpParams.fRadParams [plane][2] = stream.Get_real64 ();
fWarpParams.fRadParams [plane][3] = stream.Get_real64 ();
}
// Read the image center.
fWarpParams.fCenter.h = stream.Get_real64 ();
fWarpParams.fCenter.v = stream.Get_real64 ();
#if qDNGValidate
if (gVerbose)
{
fWarpParams.Dump ();
}
#endif
if (!fWarpParams.IsValid ())
{
ThrowBadFormat ();
}
}
/*****************************************************************************/
bool dng_opcode_WarpFisheye::IsNOP () const
{
return fWarpParams.IsNOPAll ();
}
/*****************************************************************************/
bool dng_opcode_WarpFisheye::IsValidForNegative (const dng_negative &negative) const
{
return fWarpParams.IsValidForNegative (negative);
}
/*****************************************************************************/
void dng_opcode_WarpFisheye::PutData (dng_stream &stream) const
{
const uint32 bytes = ParamBytes (fWarpParams.fPlanes);
stream.Put_uint32 (bytes);
// Write the number of planes.
stream.Put_uint32 (fWarpParams.fPlanes);
// Write the warp coefficients.
for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++)
{
stream.Put_real64 (fWarpParams.fRadParams [plane][0]);
stream.Put_real64 (fWarpParams.fRadParams [plane][1]);
stream.Put_real64 (fWarpParams.fRadParams [plane][2]);
stream.Put_real64 (fWarpParams.fRadParams [plane][3]);
}
// Write the optical center.
stream.Put_real64 (fWarpParams.fCenter.h);
stream.Put_real64 (fWarpParams.fCenter.v);
}
/*****************************************************************************/
void dng_opcode_WarpFisheye::Apply (dng_host &host,
dng_negative &negative,
AutoPtr<dng_image> &image)
{
#if qDNGValidate
dng_timer timer ("WarpFisheye time");
#endif
// Allocate destination image.
AutoPtr<dng_image> dstImage (host.Make_dng_image (image->Bounds (),
image->Planes (),
image->PixelType ()));
// Warp the image.
AutoPtr<dng_warp_params> params (new dng_warp_params_fisheye (fWarpParams));
dng_filter_warp filter (*image,
*dstImage,
negative,
params);
filter.Initialize (host);
host.PerformAreaTask (filter,
image->Bounds ());
// Return the new image.
image.Reset (dstImage.Release ());
}
/*****************************************************************************/
uint32 dng_opcode_WarpFisheye::ParamBytes (uint32 planes)
{
return (1 * (uint32) sizeof (uint32) ) + // Number of planes.
(4 * (uint32) sizeof (real64) * planes) + // Warp coefficients.
(2 * (uint32) sizeof (real64) ); // Optical center.
}
/*****************************************************************************/
dng_vignette_radial_params::dng_vignette_radial_params ()
: fParams (kNumTerms)
, fCenter (0.5, 0.5)
{
}
/*****************************************************************************/
dng_vignette_radial_params::dng_vignette_radial_params (const dng_std_vector<real64> ¶ms,
const dng_point_real64 ¢er)
: fParams (params)
, fCenter (center)
{
}
/*****************************************************************************/
bool dng_vignette_radial_params::IsNOP () const
{
for (uint32 i = 0; i < fParams.size (); i++)
{
if (fParams [i] != 0.0)
{
return false;
}
}
return true;
}
/*****************************************************************************/
bool dng_vignette_radial_params::IsValid () const
{
if (fParams.size () != kNumTerms)
{
return false;
}
if (fCenter.h < 0.0 ||
fCenter.h > 1.0 ||
fCenter.v < 0.0 ||
fCenter.v > 1.0)
{
return false;
}
return true;
}
/*****************************************************************************/
void dng_vignette_radial_params::Dump () const
{
#if qDNGValidate
printf (" Radial vignette params: ");
for (uint32 i = 0; i < fParams.size (); i++)
{
printf ("%s%.6lf",
(i == 0) ? "" : ", ",
fParams [i]);
}
printf ("\n");
printf (" Optical center:\n"
" h = %.6lf\n"
" v = %.6lf\n",
fCenter.h,
fCenter.v);
#endif
}
/*****************************************************************************/
class dng_vignette_radial_function: public dng_1d_function
{
protected:
const dng_vignette_radial_params fParams;
public:
explicit dng_vignette_radial_function (const dng_vignette_radial_params ¶ms)
: fParams (params)
{
}
// x represents r^2, where r is the normalized Euclidean distance from the
// optical center to a pixel. r lies in [0,1], so r^2 (and hence x) also lies
// in [0,1].
virtual real64 Evaluate (real64 x) const
{
DNG_REQUIRE (fParams.fParams.size () == dng_vignette_radial_params::kNumTerms,
"Bad number of vignette opcode coefficients.");
real64 sum = 0.0;
const dng_std_vector<real64> &v = fParams.fParams;
for (dng_std_vector<real64>::const_reverse_iterator i = v.rbegin (); i != v.rend (); i++)
{
sum = x * ((*i) + sum);
}
sum += 1.0;
return sum;
}
};
/*****************************************************************************/
dng_opcode_FixVignetteRadial::dng_opcode_FixVignetteRadial (const dng_vignette_radial_params ¶ms,
uint32 flags)
: dng_inplace_opcode (dngOpcode_FixVignetteRadial,
dngVersion_1_3_0_0,
flags)
, fParams (params)
, fImagePlanes (1)
, fSrcOriginH (0)
, fSrcOriginV (0)
, fSrcStepH (0)
, fSrcStepV (0)
, fTableInputBits (0)
, fTableOutputBits (0)
, fGainTable ()
{
if (!params.IsValid ())
{
ThrowBadFormat ();
}
}
/*****************************************************************************/
dng_opcode_FixVignetteRadial::dng_opcode_FixVignetteRadial (dng_stream &stream)
: dng_inplace_opcode (dngOpcode_FixVignetteRadial,
stream,
"FixVignetteRadial")
, fParams ()
, fImagePlanes (1)
, fSrcOriginH (0)
, fSrcOriginV (0)
, fSrcStepH (0)
, fSrcStepV (0)
, fTableInputBits (0)
, fTableOutputBits (0)
, fGainTable ()
{
// Grab the size in bytes.
const uint32 bytes = stream.Get_uint32 ();
// Verify the size.
if (bytes != ParamBytes ())
{
ThrowBadFormat ();
}
// Read vignette coefficients.
fParams.fParams = dng_std_vector<real64> (dng_vignette_radial_params::kNumTerms);
for (uint32 i = 0; i < dng_vignette_radial_params::kNumTerms; i++)
{
fParams.fParams [i] = stream.Get_real64 ();
}
// Read the image center.
fParams.fCenter.h = stream.Get_real64 ();
fParams.fCenter.v = stream.Get_real64 ();
// Debug.
#if qDNGValidate
if (gVerbose)
{
fParams.Dump ();
}
#endif
if (!fParams.IsValid ())
{
ThrowBadFormat ();
}
}
/*****************************************************************************/
bool dng_opcode_FixVignetteRadial::IsNOP () const
{
return fParams.IsNOP ();
}
/*****************************************************************************/
bool dng_opcode_FixVignetteRadial::IsValidForNegative (const dng_negative & /* negative */) const
{
return fParams.IsValid ();
}
/*****************************************************************************/
void dng_opcode_FixVignetteRadial::PutData (dng_stream &stream) const
{
const uint32 bytes = ParamBytes ();
stream.Put_uint32 (bytes);
DNG_REQUIRE (fParams.fParams.size () == dng_vignette_radial_params::kNumTerms,
"Bad number of vignette opcode coefficients.");
for (uint32 i = 0; i < dng_vignette_radial_params::kNumTerms; i++)
{
stream.Put_real64 (fParams.fParams [i]);
}
stream.Put_real64 (fParams.fCenter.h);
stream.Put_real64 (fParams.fCenter.v);
}
/*****************************************************************************/
void dng_opcode_FixVignetteRadial::Prepare (dng_negative &negative,
uint32 threadCount,
const dng_point &tileSize,
const dng_rect &imageBounds,
uint32 imagePlanes,
uint32 bufferPixelType,
dng_memory_allocator &allocator)
{
// This opcode is restricted to 32-bit images.
if (bufferPixelType != ttFloat)
{
ThrowBadFormat ();
}
// Sanity check number of planes.
DNG_ASSERT (imagePlanes >= 1 && imagePlanes <= kMaxColorPlanes,
"Bad number of planes.");
if (imagePlanes < 1 || imagePlanes > kMaxColorPlanes)
{
ThrowProgramError ();
}
fImagePlanes = imagePlanes;
// Set the vignette correction curve.
const dng_vignette_radial_function curve (fParams);
// Grab the destination image area.
const dng_rect_real64 bounds (imageBounds);
// Determine the optical center and maximum radius in pixel coordinates.
const dng_point_real64 centerPixel (Lerp_real64 (bounds.t,
bounds.b,
fParams.fCenter.v),
Lerp_real64 (bounds.l,
bounds.r,
fParams.fCenter.h));
const real64 pixelScaleV = 1.0 / negative.PixelAspectRatio ();
const real64 maxRadius = hypot (Max_real64 (Abs_real64 (centerPixel.v - bounds.t),
Abs_real64 (centerPixel.v - bounds.b)) * pixelScaleV,
Max_real64 (Abs_real64 (centerPixel.h - bounds.l),
Abs_real64 (centerPixel.h - bounds.r)));
const dng_point_real64 radius (maxRadius,
maxRadius);
// Find origin and scale.
const real64 pixelScaleH = 1.0;
fSrcOriginH = Real64ToFixed64 (-centerPixel.h * pixelScaleH / radius.h);
fSrcOriginV = Real64ToFixed64 (-centerPixel.v * pixelScaleV / radius.v);
fSrcStepH = Real64ToFixed64 (pixelScaleH / radius.h);
fSrcStepV = Real64ToFixed64 (pixelScaleV / radius.v);
// Adjust for pixel centers.
fSrcOriginH += fSrcStepH >> 1;
fSrcOriginV += fSrcStepV >> 1;
// Evaluate 32-bit vignette correction table.
dng_1d_table table32;
table32.Initialize (allocator,
curve,
false);
// Find maximum scale factor.
const real64 maxScale = Max_real32 (table32.Interpolate (0.0f),
table32.Interpolate (1.0f));
// Find table input bits.
fTableInputBits = 16;
// Find table output bits.
fTableOutputBits = 15;
while ((1 << fTableOutputBits) * maxScale > 65535.0)
{
fTableOutputBits--;
}
// Allocate 16-bit scale table.
const uint32 tableEntries = (1 << fTableInputBits) + 1;
fGainTable.Reset (allocator.Allocate (tableEntries * (uint32) sizeof (uint16)));
uint16 *table16 = fGainTable->Buffer_uint16 ();
// Interpolate 32-bit table into 16-bit table.
const real32 scale0 = 1.0f / (1 << fTableInputBits );
const real32 scale1 = 1.0f * (1 << fTableOutputBits);
for (uint32 index = 0; index < tableEntries; index++)
{
real32 x = index * scale0;
real32 y = table32.Interpolate (x) * scale1;
table16 [index] = (uint16) Round_uint32 (y);
}
// Prepare vignette mask buffers.
{
const uint32 pixelType = ttShort;
const uint32 bufferSize = ComputeBufferSize(pixelType, tileSize,
imagePlanes, pad16Bytes);
for (uint32 threadIndex = 0; threadIndex < threadCount; threadIndex++)
{
fMaskBuffers [threadIndex] . Reset (allocator.Allocate (bufferSize));
}
}
}
/*****************************************************************************/
void dng_opcode_FixVignetteRadial::ProcessArea (dng_negative & /* negative */,
uint32 threadIndex,
dng_pixel_buffer &buffer,
const dng_rect &dstArea,
const dng_rect & /* imageBounds */)
{
// Setup mask pixel buffer.
dng_pixel_buffer maskPixelBuffer(dstArea, 0, fImagePlanes, ttShort,
pcRowInterleavedAlign16,
fMaskBuffers [threadIndex]->Buffer ());
// Compute mask.
DoVignetteMask16 (maskPixelBuffer.DirtyPixel_uint16 (dstArea.t, dstArea.l),
dstArea.H (),
dstArea.W (),
maskPixelBuffer.RowStep (),
fSrcOriginH + fSrcStepH * dstArea.l,
fSrcOriginV + fSrcStepV * dstArea.t,
fSrcStepH,
fSrcStepV,
fTableInputBits,
fGainTable->Buffer_uint16 ());
// Apply mask.
DoVignette32 (buffer.DirtyPixel_real32 (dstArea.t, dstArea.l),
maskPixelBuffer.ConstPixel_uint16 (dstArea.t, dstArea.l),
dstArea.H (),
dstArea.W (),
fImagePlanes,
buffer.RowStep (),
buffer.PlaneStep (),
maskPixelBuffer.RowStep (),
fTableOutputBits);
}
/*****************************************************************************/
uint32 dng_opcode_FixVignetteRadial::ParamBytes ()
{
const uint32 N = dng_vignette_radial_params::kNumTerms;
return ((N * sizeof (real64)) + // Vignette coefficients.
(2 * sizeof (real64))); // Optical center.
}
/*****************************************************************************/