/*****************************************************************************/
// Copyright 2008-2009 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_gain_map.cpp#1 $ */
/* $DateTime: 2012/05/30 13:28:51 $ */
/* $Change: 832332 $ */
/* $Author: tknoll $ */
/*****************************************************************************/
#include "dng_gain_map.h"
#include "dng_exceptions.h"
#include "dng_globals.h"
#include "dng_host.h"
#include "dng_pixel_buffer.h"
#include "dng_safe_arithmetic.h"
#include "dng_stream.h"
#include "dng_tag_values.h"
/*****************************************************************************/
class dng_gain_map_interpolator
{
private:
const dng_gain_map &fMap;
dng_point_real64 fScale;
dng_point_real64 fOffset;
int32 fColumn;
int32 fPlane;
uint32 fRowIndex1;
uint32 fRowIndex2;
real32 fRowFract;
int32 fResetColumn;
real32 fValueBase;
real32 fValueStep;
real32 fValueIndex;
public:
dng_gain_map_interpolator (const dng_gain_map &map,
const dng_rect &mapBounds,
int32 row,
int32 column,
uint32 plane);
real32 Interpolate () const
{
return fValueBase + fValueStep * fValueIndex;
}
void Increment ()
{
if (++fColumn >= fResetColumn)
{
ResetColumn ();
}
else
{
fValueIndex += 1.0f;
}
}
private:
real32 InterpolateEntry (uint32 colIndex);
void ResetColumn ();
};
/*****************************************************************************/
dng_gain_map_interpolator::dng_gain_map_interpolator (const dng_gain_map &map,
const dng_rect &mapBounds,
int32 row,
int32 column,
uint32 plane)
: fMap (map)
, fScale (1.0 / mapBounds.H (),
1.0 / mapBounds.W ())
, fOffset (0.5 - mapBounds.t,
0.5 - mapBounds.l)
, fColumn (column)
, fPlane (plane)
, fRowIndex1 (0)
, fRowIndex2 (0)
, fRowFract (0.0f)
, fResetColumn (0)
, fValueBase (0.0f)
, fValueStep (0.0f)
, fValueIndex (0.0f)
{
real64 rowIndexF = (fScale.v * (row + fOffset.v) -
fMap.Origin ().v) / fMap.Spacing ().v;
if (rowIndexF <= 0.0)
{
fRowIndex1 = 0;
fRowIndex2 = 0;
fRowFract = 0.0f;
}
else
{
if (fMap.Points ().v < 1)
{
ThrowProgramError ("Empty gain map");
}
uint32 lastRow = static_cast<uint32> (fMap.Points ().v - 1);
if (rowIndexF >= static_cast<real64> (lastRow))
{
fRowIndex1 = lastRow;
fRowIndex2 = fRowIndex1;
fRowFract = 0.0f;
}
else
{
// If we got here, we know that rowIndexF can safely be converted to
// a uint32 and that static_cast<uint32> (rowIndexF) < lastRow. This
// implies fRowIndex2 <= lastRow below.
fRowIndex1 = static_cast<uint32> (rowIndexF);
fRowIndex2 = fRowIndex1 + 1;
fRowFract = (real32) (rowIndexF - (real64) fRowIndex1);
}
}
ResetColumn ();
}
/*****************************************************************************/
real32 dng_gain_map_interpolator::InterpolateEntry (uint32 colIndex)
{
return fMap.Entry (fRowIndex1, colIndex, fPlane) * (1.0f - fRowFract) +
fMap.Entry (fRowIndex2, colIndex, fPlane) * ( fRowFract);
}
/*****************************************************************************/
void dng_gain_map_interpolator::ResetColumn ()
{
real64 colIndexF = ((fScale.h * (fColumn + fOffset.h)) -
fMap.Origin ().h) / fMap.Spacing ().h;
if (colIndexF <= 0.0)
{
fValueBase = InterpolateEntry (0);
fValueStep = 0.0f;
fResetColumn = (int32) ceil (fMap.Origin ().h / fScale.h - fOffset.h);
}
else
{
if (fMap.Points ().h < 1)
{
ThrowProgramError ("Empty gain map");
}
uint32 lastCol = static_cast<uint32> (fMap.Points ().h - 1);
if (colIndexF >= static_cast<real64> (lastCol))
{
fValueBase = InterpolateEntry (lastCol);
fValueStep = 0.0f;
fResetColumn = 0x7FFFFFFF;
}
else
{
// If we got here, we know that colIndexF can safely be converted to
// a uint32 and that static_cast<uint32> (colIndexF) < lastCol. This
// implies colIndex + 1 <= lastCol, i.e. the argument to
// InterpolateEntry() below is valid.
uint32 colIndex = static_cast<uint32> (colIndexF);
real64 base = InterpolateEntry (colIndex);
real64 delta = InterpolateEntry (colIndex + 1) - base;
fValueBase = (real32) (base + delta * (colIndexF - (real64) colIndex));
fValueStep = (real32) ((delta * fScale.h) / fMap.Spacing ().h);
fResetColumn = (int32) ceil (((colIndex + 1) * fMap.Spacing ().h +
fMap.Origin ().h) / fScale.h - fOffset.h);
}
}
fValueIndex = 0.0f;
}
/*****************************************************************************/
dng_gain_map::dng_gain_map (dng_memory_allocator &allocator,
const dng_point &points,
const dng_point_real64 &spacing,
const dng_point_real64 &origin,
uint32 planes)
: fPoints (points)
, fSpacing (spacing)
, fOrigin (origin)
, fPlanes (planes)
, fRowStep (SafeUint32Mult(planes, points.h))
, fBuffer ()
{
fBuffer.Reset (allocator.Allocate (
ComputeBufferSize (ttFloat, fPoints, fPlanes, pad16Bytes)));
}
/*****************************************************************************/
real32 dng_gain_map::Interpolate (int32 row,
int32 col,
uint32 plane,
const dng_rect &bounds) const
{
dng_gain_map_interpolator interp (*this,
bounds,
row,
col,
plane);
return interp.Interpolate ();
}
/*****************************************************************************/
uint32 dng_gain_map::PutStreamSize () const
{
return 44 + fPoints.v * fPoints.h * fPlanes * 4;
}
/*****************************************************************************/
void dng_gain_map::PutStream (dng_stream &stream) const
{
stream.Put_uint32 (fPoints.v);
stream.Put_uint32 (fPoints.h);
stream.Put_real64 (fSpacing.v);
stream.Put_real64 (fSpacing.h);
stream.Put_real64 (fOrigin.v);
stream.Put_real64 (fOrigin.h);
stream.Put_uint32 (fPlanes);
for (int32 rowIndex = 0; rowIndex < fPoints.v; rowIndex++)
{
for (int32 colIndex = 0; colIndex < fPoints.h; colIndex++)
{
for (uint32 plane = 0; plane < fPlanes; plane++)
{
stream.Put_real32 (Entry (rowIndex,
colIndex,
plane));
}
}
}
}
/*****************************************************************************/
dng_gain_map * dng_gain_map::GetStream (dng_host &host,
dng_stream &stream)
{
dng_point mapPoints;
mapPoints.v = stream.Get_uint32 ();
mapPoints.h = stream.Get_uint32 ();
dng_point_real64 mapSpacing;
mapSpacing.v = stream.Get_real64 ();
mapSpacing.h = stream.Get_real64 ();
dng_point_real64 mapOrigin;
mapOrigin.v = stream.Get_real64 ();
mapOrigin.h = stream.Get_real64 ();
uint32 mapPlanes = stream.Get_uint32 ();
#if qDNGValidate
if (gVerbose)
{
printf ("Points: v=%d, h=%d\n",
(int) mapPoints.v,
(int) mapPoints.h);
printf ("Spacing: v=%.6f, h=%.6f\n",
mapSpacing.v,
mapSpacing.h);
printf ("Origin: v=%.6f, h=%.6f\n",
mapOrigin.v,
mapOrigin.h);
printf ("Planes: %u\n",
(unsigned) mapPlanes);
}
#endif
if (mapPoints.v == 1)
{
mapSpacing.v = 1.0;
mapOrigin.v = 0.0;
}
if (mapPoints.h == 1)
{
mapSpacing.h = 1.0;
mapOrigin.h = 0.0;
}
if (mapPoints.v < 1 ||
mapPoints.h < 1 ||
mapSpacing.v <= 0.0 ||
mapSpacing.h <= 0.0 ||
mapPlanes < 1)
{
ThrowBadFormat ();
}
AutoPtr<dng_gain_map> map (new dng_gain_map (host.Allocator (),
mapPoints,
mapSpacing,
mapOrigin,
mapPlanes));
#if qDNGValidate
uint32 linesPrinted = 0;
uint32 linesSkipped = 0;
#endif
for (int32 rowIndex = 0; rowIndex < mapPoints.v; rowIndex++)
{
for (int32 colIndex = 0; colIndex < mapPoints.h; colIndex++)
{
for (uint32 plane = 0; plane < mapPlanes; plane++)
{
real32 x = stream.Get_real32 ();
map->Entry (rowIndex, colIndex, plane) = x;
#if qDNGValidate
if (gVerbose)
{
if (linesPrinted < gDumpLineLimit)
{
printf (" Map [%3u] [%3u] [%u] = %.4f\n",
(unsigned) rowIndex,
(unsigned) colIndex,
(unsigned) plane,
x);
linesPrinted++;
}
else
linesSkipped++;
}
#endif
}
}
}
#if qDNGValidate
if (linesSkipped)
{
printf (" ... %u map entries skipped\n", (unsigned) linesSkipped);
}
#endif
return map.Release ();
}
/*****************************************************************************/
dng_opcode_GainMap::dng_opcode_GainMap (const dng_area_spec &areaSpec,
AutoPtr<dng_gain_map> &gainMap)
: dng_inplace_opcode (dngOpcode_GainMap,
dngVersion_1_3_0_0,
kFlag_None)
, fAreaSpec (areaSpec)
, fGainMap ()
{
fGainMap.Reset (gainMap.Release ());
}
/*****************************************************************************/
dng_opcode_GainMap::dng_opcode_GainMap (dng_host &host,
dng_stream &stream)
: dng_inplace_opcode (dngOpcode_GainMap,
stream,
"GainMap")
, fAreaSpec ()
, fGainMap ()
{
uint32 byteCount = stream.Get_uint32 ();
uint64 startPosition = stream.Position ();
fAreaSpec.GetData (stream);
fGainMap.Reset (dng_gain_map::GetStream (host, stream));
if (stream.Position () != startPosition + byteCount)
{
ThrowBadFormat ();
}
}
/*****************************************************************************/
void dng_opcode_GainMap::PutData (dng_stream &stream) const
{
stream.Put_uint32 (dng_area_spec::kDataSize +
fGainMap->PutStreamSize ());
fAreaSpec.PutData (stream);
fGainMap->PutStream (stream);
}
/*****************************************************************************/
void dng_opcode_GainMap::ProcessArea (dng_negative & /* negative */,
uint32 /* threadIndex */,
dng_pixel_buffer &buffer,
const dng_rect &dstArea,
const dng_rect &imageBounds)
{
dng_rect overlap = fAreaSpec.Overlap (dstArea);
if (overlap.NotEmpty ())
{
uint32 cols = overlap.W ();
uint32 colPitch = fAreaSpec.ColPitch ();
for (uint32 plane = fAreaSpec.Plane ();
plane < fAreaSpec.Plane () + fAreaSpec.Planes () &&
plane < buffer.Planes ();
plane++)
{
uint32 mapPlane = Min_uint32 (plane, fGainMap->Planes () - 1);
for (int32 row = overlap.t; row < overlap.b; row += fAreaSpec.RowPitch ())
{
real32 *dPtr = buffer.DirtyPixel_real32 (row, overlap.l, plane);
dng_gain_map_interpolator interp (*fGainMap,
imageBounds,
row,
overlap.l,
mapPlane);
for (uint32 col = 0; col < cols; col += colPitch)
{
real32 gain = interp.Interpolate ();
dPtr [col] = Min_real32 (dPtr [col] * gain, 1.0f);
for (uint32 j = 0; j < colPitch; j++)
{
interp.Increment ();
}
}
}
}
}
}
/*****************************************************************************/