| /*****************************************************************************/ |
| // 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 (); |
| } |
| |
| } |
| |
| } |
| |
| } |
| |
| } |
| |
| } |
| |
| /*****************************************************************************/ |