| /*****************************************************************************/ |
| // Copyright 2006 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_resample.cpp#1 $ */ |
| /* $DateTime: 2012/05/30 13:28:51 $ */ |
| /* $Change: 832332 $ */ |
| /* $Author: tknoll $ */ |
| |
| /*****************************************************************************/ |
| |
| #include "dng_resample.h" |
| |
| #include "dng_assertions.h" |
| #include "dng_bottlenecks.h" |
| #include "dng_filter_task.h" |
| #include "dng_host.h" |
| #include "dng_image.h" |
| #include "dng_memory.h" |
| #include "dng_pixel_buffer.h" |
| #include "dng_safe_arithmetic.h" |
| #include "dng_tag_types.h" |
| #include "dng_utils.h" |
| |
| /******************************************************************************/ |
| |
| real64 dng_resample_bicubic::Extent () const |
| { |
| |
| return 2.0; |
| |
| } |
| |
| /******************************************************************************/ |
| |
| real64 dng_resample_bicubic::Evaluate (real64 x) const |
| { |
| |
| const real64 A = -0.75; |
| |
| x = Abs_real64 (x); |
| |
| if (x >= 2.0) |
| return 0.0; |
| |
| else if (x >= 1.0) |
| return (((A * x - 5.0 * A) * x + 8.0 * A) * x - 4.0 * A); |
| |
| else |
| return (((A + 2.0) * x - (A + 3.0)) * x * x + 1.0); |
| |
| } |
| |
| /******************************************************************************/ |
| |
| const dng_resample_function & dng_resample_bicubic::Get () |
| { |
| |
| static dng_resample_bicubic static_dng_resample_bicubic; |
| |
| return static_dng_resample_bicubic; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_resample_coords::dng_resample_coords () |
| |
| : fOrigin (0) |
| , fCoords () |
| |
| { |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_resample_coords::~dng_resample_coords () |
| { |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_resample_coords::Initialize (int32 srcOrigin, |
| int32 dstOrigin, |
| uint32 srcCount, |
| uint32 dstCount, |
| dng_memory_allocator &allocator) |
| { |
| |
| fOrigin = dstOrigin; |
| |
| uint32 dstEntries = 0; |
| uint32 bufferSize = 0; |
| if (!RoundUpUint32ToMultiple(dstCount, 8, &dstEntries) || |
| !SafeUint32Mult(dstEntries, sizeof(int32), &bufferSize)) { |
| ThrowMemoryFull("Arithmetic overflow computing size for coordinate " |
| "buffer"); |
| } |
| fCoords.Reset (allocator.Allocate (bufferSize)); |
| |
| int32 *coords = fCoords->Buffer_int32 (); |
| |
| real64 invScale = (real64) srcCount / |
| (real64) dstCount; |
| |
| for (uint32 j = 0; j < dstCount; j++) |
| { |
| |
| real64 x = (real64) j + 0.5; |
| |
| real64 y = x * invScale - 0.5 + (real64) srcOrigin; |
| |
| coords [j] = Round_int32 (y * (real64) kResampleSubsampleCount); |
| |
| } |
| |
| // Pad out table by replicating last entry. |
| |
| for (uint32 k = dstCount; k < dstEntries; k++) |
| { |
| |
| coords [k] = coords [dstCount - 1]; |
| |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_resample_weights::dng_resample_weights () |
| |
| : fRadius (0) |
| |
| , fWeightStep (0) |
| |
| , fWeights32 () |
| , fWeights16 () |
| |
| { |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_resample_weights::~dng_resample_weights () |
| { |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_resample_weights::Initialize (real64 scale, |
| const dng_resample_function &kernel, |
| dng_memory_allocator &allocator) |
| { |
| |
| uint32 j; |
| |
| // We only adjust the kernel size for scale factors less than 1.0. |
| |
| scale = Min_real64 (scale, 1.0); |
| |
| // Find radius of this kernel. |
| |
| fRadius = (uint32) (kernel.Extent () / scale + 0.9999); |
| |
| // Width is twice the radius. |
| |
| uint32 width = fRadius * 2; |
| |
| // Round to each set to weights to a multiple of 8 entries. |
| |
| if (!RoundUpUint32ToMultiple (width, 8, &fWeightStep)) |
| { |
| |
| ThrowMemoryFull ("Arithmetic overflow computing fWeightStep"); |
| |
| } |
| |
| // Allocate and zero weight tables. |
| |
| uint32 bufferSize = 0; |
| |
| if (!SafeUint32Mult (fWeightStep, kResampleSubsampleCount, &bufferSize) || |
| !SafeUint32Mult (bufferSize, (uint32) sizeof (real32), &bufferSize)) |
| { |
| |
| ThrowMemoryFull("Arithmetic overflow computing buffer size."); |
| |
| } |
| |
| fWeights32.Reset (allocator.Allocate (bufferSize)); |
| |
| DoZeroBytes (fWeights32->Buffer (), |
| fWeights32->LogicalSize ()); |
| |
| if (!SafeUint32Mult (fWeightStep, kResampleSubsampleCount, &bufferSize) || |
| !SafeUint32Mult (bufferSize, (uint32) sizeof (int16), &bufferSize)) |
| { |
| |
| ThrowMemoryFull("Arithmetic overflow computing buffer size."); |
| |
| } |
| |
| fWeights16.Reset (allocator.Allocate (bufferSize)); |
| |
| DoZeroBytes (fWeights16->Buffer (), |
| fWeights16->LogicalSize ()); |
| |
| // Compute kernel for each subsample values. |
| |
| for (uint32 sample = 0; sample < kResampleSubsampleCount; sample++) |
| { |
| |
| real64 fract = sample * (1.0 / (real64) kResampleSubsampleCount); |
| |
| real32 *w32 = fWeights32->Buffer_real32 () + fWeightStep * sample; |
| |
| // Evaluate kernel function for 32 bit weights. |
| |
| { |
| |
| real64 t32 = 0.0; |
| |
| for (j = 0; j < width; j++) |
| { |
| |
| int32 k = (int32) j - (int32) fRadius + 1; |
| |
| real64 x = (k - fract) * scale; |
| |
| w32 [j] = (real32) kernel.Evaluate (x); |
| |
| t32 += w32 [j]; |
| |
| } |
| |
| // Scale 32 bit weights so total of weights is 1.0. |
| |
| real32 s32 = (real32) (1.0 / t32); |
| |
| for (j = 0; j < width; j++) |
| { |
| |
| w32 [j] *= s32; |
| |
| } |
| |
| } |
| |
| // Round off 32 bit weights to 16 bit weights. |
| |
| { |
| |
| int16 *w16 = fWeights16->Buffer_int16 () + fWeightStep * sample; |
| |
| int32 t16 = 0; |
| |
| for (j = 0; j < width; j++) |
| { |
| |
| w16 [j] = (int16) Round_int32 (w32 [j] * 16384.0); |
| |
| t16 += w16 [j]; |
| |
| } |
| |
| // Adjust center entry for any round off error so total is |
| // exactly 16384. |
| |
| w16 [fRadius - (fract >= 0.5 ? 0 : 1)] += (int16) (16384 - t16); |
| |
| } |
| |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_resample_weights_2d::dng_resample_weights_2d () |
| |
| : fRadius (0) |
| |
| , fRowStep (0) |
| , fColStep (0) |
| |
| , fWeights32 () |
| , fWeights16 () |
| |
| { |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_resample_weights_2d::~dng_resample_weights_2d () |
| { |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_resample_weights_2d::Initialize (const dng_resample_function &kernel, |
| dng_memory_allocator &allocator) |
| { |
| |
| // Find radius of this kernel. Unlike with 1d resample weights (see |
| // dng_resample_weights), we never scale up the kernel size. |
| |
| fRadius = (uint32) (kernel.Extent () + 0.9999); |
| |
| // Width is twice the radius. |
| |
| uint32 width = 0; |
| uint32 widthSqr = 0; |
| uint32 step = 0; |
| |
| if (!SafeUint32Mult (fRadius, 2, &width) || |
| !SafeUint32Mult (width, width, &widthSqr) || |
| !RoundUpUint32ToMultiple (widthSqr, 8, &step) || |
| !SafeUint32Mult (step, kResampleSubsampleCount2D, &fRowStep)) |
| { |
| |
| ThrowMemoryFull ("Arithmetic overflow computing row step."); |
| |
| } |
| |
| fColStep = step; |
| |
| // Allocate and zero weight tables. |
| |
| uint32 bufferSize = 0; |
| |
| if (!SafeUint32Mult (step, kResampleSubsampleCount2D, &bufferSize) || |
| !SafeUint32Mult (bufferSize, kResampleSubsampleCount2D, &bufferSize) || |
| !SafeUint32Mult (bufferSize, (uint32) sizeof (real32), &bufferSize)) |
| { |
| |
| ThrowMemoryFull ("Arithmetic overflow computing buffer size."); |
| |
| } |
| |
| fWeights32.Reset (allocator.Allocate (bufferSize)); |
| |
| DoZeroBytes (fWeights32->Buffer (), |
| fWeights32->LogicalSize ()); |
| |
| |
| if (!SafeUint32Mult (step, kResampleSubsampleCount2D, &bufferSize) || |
| !SafeUint32Mult (bufferSize, kResampleSubsampleCount2D, &bufferSize) || |
| !SafeUint32Mult (bufferSize, (uint32) sizeof (int16), &bufferSize)) |
| { |
| |
| ThrowMemoryFull ("Arithmetic overflow computing buffer size."); |
| |
| } |
| |
| fWeights16.Reset (allocator.Allocate (bufferSize)); |
| |
| DoZeroBytes (fWeights16->Buffer (), |
| fWeights16->LogicalSize ()); |
| |
| // Compute kernel for each subsample values. |
| |
| for (uint32 y = 0; y < kResampleSubsampleCount2D; y++) |
| { |
| |
| real64 yFract = y * (1.0 / (real64) kResampleSubsampleCount2D); |
| |
| for (uint32 x = 0; x < kResampleSubsampleCount2D; x++) |
| { |
| |
| real64 xFract = x * (1.0 / (real64) kResampleSubsampleCount2D); |
| |
| real32 *w32 = (real32 *) Weights32 (dng_point ((int32) y, |
| (int32) x)); |
| |
| // Evaluate kernel function for 32 bit weights. |
| |
| { |
| |
| real64 t32 = 0.0; |
| |
| uint32 index = 0; |
| |
| for (uint32 i = 0; i < width; i++) |
| { |
| |
| int32 yInt = ((int32) i) - (int32) fRadius + 1; |
| real64 yPos = yInt - yFract; |
| |
| for (uint32 j = 0; j < width; j++) |
| { |
| |
| int32 xInt = ((int32) j) - (int32) fRadius + 1; |
| real64 xPos = xInt - xFract; |
| |
| #if 0 |
| |
| // Radial. |
| |
| real64 dy2 = yPos * yPos; |
| real64 dx2 = xPos * xPos; |
| |
| real64 r = sqrt (dx2 + dy2); |
| |
| w32 [index] = (real32) kernel.Evaluate (r); |
| |
| #else |
| |
| // Separable. |
| |
| w32 [index] = (real32) kernel.Evaluate (xPos) * |
| (real32) kernel.Evaluate (yPos); |
| |
| #endif |
| |
| t32 += w32 [index]; |
| |
| index++; |
| |
| } |
| |
| } |
| |
| // Scale 32 bit weights so total of weights is 1.0. |
| |
| const real32 s32 = (real32) (1.0 / t32); |
| |
| for (uint32 i = 0; i < widthSqr; i++) |
| { |
| |
| w32 [i] *= s32; |
| |
| } |
| |
| } |
| |
| // Round off 32 bit weights to 16 bit weights. |
| |
| { |
| |
| int16 *w16 = (int16 *) Weights16 (dng_point ((int32) y, |
| (int32) x)); |
| |
| int32 t16 = 0; |
| |
| for (uint32 j = 0; j < widthSqr; j++) |
| { |
| |
| w16 [j] = (int16) Round_int32 (w32 [j] * 16384.0); |
| |
| t16 += w16 [j]; |
| |
| } |
| |
| // Adjust one of the center entries for any round off error so total |
| // is exactly 16384. |
| |
| const uint32 xOffset = fRadius - ((xFract >= 0.5) ? 0 : 1); |
| const uint32 yOffset = fRadius - ((yFract >= 0.5) ? 0 : 1); |
| const uint32 centerOffset = width * yOffset + xOffset; |
| |
| w16 [centerOffset] += (int16) (16384 - t16); |
| |
| } |
| |
| } |
| |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| class dng_resample_task: public dng_filter_task |
| { |
| |
| protected: |
| |
| dng_rect fSrcBounds; |
| dng_rect fDstBounds; |
| |
| const dng_resample_function &fKernel; |
| |
| real64 fRowScale; |
| real64 fColScale; |
| |
| dng_resample_coords fRowCoords; |
| dng_resample_coords fColCoords; |
| |
| dng_resample_weights fWeightsV; |
| dng_resample_weights fWeightsH; |
| |
| dng_point fSrcTileSize; |
| |
| AutoPtr<dng_memory_block> fTempBuffer [kMaxMPThreads]; |
| |
| public: |
| |
| dng_resample_task (const dng_image &srcImage, |
| dng_image &dstImage, |
| const dng_rect &srcBounds, |
| const dng_rect &dstBounds, |
| const dng_resample_function &kernel); |
| |
| virtual dng_rect SrcArea (const dng_rect &dstArea); |
| |
| virtual dng_point SrcTileSize (const dng_point &dstTileSize); |
| |
| virtual void Start (uint32 threadCount, |
| const dng_point &tileSize, |
| dng_memory_allocator *allocator, |
| dng_abort_sniffer *sniffer); |
| |
| virtual void ProcessArea (uint32 threadIndex, |
| dng_pixel_buffer &srcBuffer, |
| dng_pixel_buffer &dstBuffer); |
| |
| }; |
| |
| /*****************************************************************************/ |
| |
| dng_resample_task::dng_resample_task (const dng_image &srcImage, |
| dng_image &dstImage, |
| const dng_rect &srcBounds, |
| const dng_rect &dstBounds, |
| const dng_resample_function &kernel) |
| |
| : dng_filter_task (srcImage, |
| dstImage) |
| |
| , fSrcBounds (srcBounds) |
| , fDstBounds (dstBounds) |
| |
| , fKernel (kernel) |
| |
| , fRowScale ((srcBounds.H () != 0) ? dstBounds.H () / (real64) srcBounds.H () : 0) |
| , fColScale ((srcBounds.W () != 0) ? dstBounds.W () / (real64) srcBounds.W () : 0) |
| |
| , fRowCoords () |
| , fColCoords () |
| |
| , fWeightsV () |
| , fWeightsH () |
| |
| , fSrcTileSize () |
| |
| { |
| if (fRowScale == 0 || fColScale == 0) |
| { |
| ThrowBadFormat (); |
| } |
| |
| if (srcImage.PixelSize () <= 2 && |
| dstImage.PixelSize () <= 2 && |
| srcImage.PixelRange () == dstImage.PixelRange ()) |
| { |
| fSrcPixelType = ttShort; |
| fDstPixelType = ttShort; |
| } |
| |
| else |
| { |
| fSrcPixelType = ttFloat; |
| fDstPixelType = ttFloat; |
| } |
| |
| fUnitCell = dng_point (8, 8); |
| |
| fMaxTileSize.v = Pin_int32 (fUnitCell.v, |
| Round_int32 (fMaxTileSize.v * fRowScale), |
| fMaxTileSize.v); |
| |
| fMaxTileSize.h = Pin_int32 (fUnitCell.h, |
| Round_int32 (fMaxTileSize.h * fColScale), |
| fMaxTileSize.h); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_rect dng_resample_task::SrcArea (const dng_rect &dstArea) |
| { |
| |
| int32 offsetV = fWeightsV.Offset (); |
| int32 offsetH = fWeightsH.Offset (); |
| |
| uint32 widthV = fWeightsV.Width (); |
| uint32 widthH = fWeightsH.Width (); |
| |
| dng_rect srcArea; |
| |
| srcArea.t = SafeInt32Add (fRowCoords.Pixel (dstArea.t), offsetV); |
| srcArea.l = SafeInt32Add (fColCoords.Pixel (dstArea.l), offsetH); |
| |
| srcArea.b = SafeInt32Add (SafeInt32Add ( |
| fRowCoords.Pixel (SafeInt32Sub (dstArea.b, 1)), |
| offsetV), |
| ConvertUint32ToInt32 (widthV));; |
| srcArea.r = SafeInt32Add(SafeInt32Add( |
| fColCoords.Pixel (SafeInt32Sub (dstArea.r, 1)), |
| offsetH), |
| ConvertUint32ToInt32(widthH));; |
| |
| return srcArea; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_point dng_resample_task::SrcTileSize (const dng_point & /* dstTileSize */) |
| { |
| |
| return fSrcTileSize; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_resample_task::Start (uint32 threadCount, |
| const dng_point &tileSize, |
| dng_memory_allocator *allocator, |
| dng_abort_sniffer *sniffer) |
| { |
| |
| // Compute sub-pixel resolution coordinates in the source image for |
| // each row and column of the destination area. |
| |
| fRowCoords.Initialize (fSrcBounds.t, |
| fDstBounds.t, |
| fSrcBounds.H (), |
| fDstBounds.H (), |
| *allocator); |
| |
| fColCoords.Initialize (fSrcBounds.l, |
| fDstBounds.l, |
| fSrcBounds.W (), |
| fDstBounds.W (), |
| *allocator); |
| |
| // Compute resampling kernels. |
| |
| fWeightsV.Initialize (fRowScale, |
| fKernel, |
| *allocator); |
| |
| fWeightsH.Initialize (fColScale, |
| fKernel, |
| *allocator); |
| |
| // Find upper bound on source source tile. |
| |
| fSrcTileSize.v = Round_int32 (tileSize.v / fRowScale) + fWeightsV.Width () + 2; |
| fSrcTileSize.h = Round_int32 (tileSize.h / fColScale) + fWeightsH.Width () + 2; |
| |
| // Allocate temp buffers. |
| |
| uint32 tempBufferSize = 0; |
| if (!RoundUpUint32ToMultiple (fSrcTileSize.h, 8, &tempBufferSize) || |
| !SafeUint32Mult (tempBufferSize, |
| static_cast<uint32> (sizeof (real32)), |
| &tempBufferSize)) |
| { |
| |
| ThrowMemoryFull("Arithmetic overflow computing buffer size."); |
| |
| } |
| |
| for (uint32 threadIndex = 0; threadIndex < threadCount; threadIndex++) |
| { |
| |
| fTempBuffer [threadIndex] . Reset (allocator->Allocate (tempBufferSize)); |
| |
| } |
| |
| // Allocate the pixel buffers. |
| |
| dng_filter_task::Start (threadCount, |
| tileSize, |
| allocator, |
| sniffer); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_resample_task::ProcessArea (uint32 threadIndex, |
| dng_pixel_buffer &srcBuffer, |
| dng_pixel_buffer &dstBuffer) |
| { |
| |
| dng_rect srcArea = srcBuffer.fArea; |
| dng_rect dstArea = dstBuffer.fArea; |
| |
| uint32 srcCols = srcArea.W (); |
| uint32 dstCols = dstArea.W (); |
| |
| uint32 widthV = fWeightsV.Width (); |
| uint32 widthH = fWeightsH.Width (); |
| |
| int32 offsetV = fWeightsV.Offset (); |
| int32 offsetH = fWeightsH.Offset (); |
| |
| uint32 stepH = fWeightsH.Step (); |
| |
| const int32 *rowCoords = fRowCoords.Coords (0 ); |
| const int32 *colCoords = fColCoords.Coords (dstArea.l); |
| |
| if (fSrcPixelType == ttFloat) |
| { |
| |
| const real32 *weightsH = fWeightsH.Weights32 (0); |
| |
| real32 *tPtr = fTempBuffer [threadIndex]->Buffer_real32 (); |
| |
| real32 *ttPtr = tPtr + offsetH - srcArea.l; |
| |
| for (int32 dstRow = dstArea.t; dstRow < dstArea.b; dstRow++) |
| { |
| |
| int32 rowCoord = rowCoords [dstRow]; |
| |
| int32 rowFract = rowCoord & kResampleSubsampleMask; |
| |
| const real32 *weightsV = fWeightsV.Weights32 (rowFract); |
| |
| int32 srcRow = (rowCoord >> kResampleSubsampleBits) + offsetV; |
| |
| for (uint32 plane = 0; plane < dstBuffer.fPlanes; plane++) |
| { |
| |
| const real32 *sPtr = srcBuffer.ConstPixel_real32 (srcRow, |
| srcArea.l, |
| plane); |
| |
| DoResampleDown32 (sPtr, |
| tPtr, |
| srcCols, |
| srcBuffer.fRowStep, |
| weightsV, |
| widthV); |
| |
| real32 *dPtr = dstBuffer.DirtyPixel_real32 (dstRow, |
| dstArea.l, |
| plane); |
| |
| DoResampleAcross32 (ttPtr, |
| dPtr, |
| dstCols, |
| colCoords, |
| weightsH, |
| widthH, |
| stepH); |
| |
| } |
| |
| } |
| |
| } |
| |
| else |
| { |
| |
| const int16 *weightsH = fWeightsH.Weights16 (0); |
| |
| uint16 *tPtr = fTempBuffer [threadIndex]->Buffer_uint16 (); |
| |
| uint16 *ttPtr = tPtr + offsetH - srcArea.l; |
| |
| uint32 pixelRange = fDstImage.PixelRange (); |
| |
| for (int32 dstRow = dstArea.t; dstRow < dstArea.b; dstRow++) |
| { |
| |
| int32 rowCoord = rowCoords [dstRow]; |
| |
| int32 rowFract = rowCoord & kResampleSubsampleMask; |
| |
| const int16 *weightsV = fWeightsV.Weights16 (rowFract); |
| |
| int32 srcRow = (rowCoord >> kResampleSubsampleBits) + offsetV; |
| |
| for (uint32 plane = 0; plane < dstBuffer.fPlanes; plane++) |
| { |
| |
| const uint16 *sPtr = srcBuffer.ConstPixel_uint16 (srcRow, |
| srcArea.l, |
| plane); |
| |
| DoResampleDown16 (sPtr, |
| tPtr, |
| srcCols, |
| srcBuffer.fRowStep, |
| weightsV, |
| widthV, |
| pixelRange); |
| |
| uint16 *dPtr = dstBuffer.DirtyPixel_uint16 (dstRow, |
| dstArea.l, |
| plane); |
| |
| DoResampleAcross16 (ttPtr, |
| dPtr, |
| dstCols, |
| colCoords, |
| weightsH, |
| widthH, |
| stepH, |
| pixelRange); |
| |
| } |
| |
| } |
| |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| void ResampleImage (dng_host &host, |
| const dng_image &srcImage, |
| dng_image &dstImage, |
| const dng_rect &srcBounds, |
| const dng_rect &dstBounds, |
| const dng_resample_function &kernel) |
| { |
| |
| dng_resample_task task (srcImage, |
| dstImage, |
| srcBounds, |
| dstBounds, |
| kernel); |
| |
| host.PerformAreaTask (task, |
| dstBounds); |
| |
| } |
| |
| /*****************************************************************************/ |