Home | History | Annotate | Download | only in source
      1 /*****************************************************************************/
      2 // Copyright 2006 Adobe Systems Incorporated
      3 // All Rights Reserved.
      4 //
      5 // NOTICE:  Adobe permits you to use, modify, and distribute this file in
      6 // accordance with the terms of the Adobe license agreement accompanying it.
      7 /*****************************************************************************/
      8 
      9 /* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_resample.cpp#1 $ */
     10 /* $DateTime: 2012/05/30 13:28:51 $ */
     11 /* $Change: 832332 $ */
     12 /* $Author: tknoll $ */
     13 
     14 /*****************************************************************************/
     15 
     16 #include "dng_resample.h"
     17 
     18 #include "dng_assertions.h"
     19 #include "dng_bottlenecks.h"
     20 #include "dng_filter_task.h"
     21 #include "dng_host.h"
     22 #include "dng_image.h"
     23 #include "dng_memory.h"
     24 #include "dng_pixel_buffer.h"
     25 #include "dng_safe_arithmetic.h"
     26 #include "dng_tag_types.h"
     27 #include "dng_utils.h"
     28 
     29 /******************************************************************************/
     30 
     31 real64 dng_resample_bicubic::Extent () const
     32 	{
     33 
     34 	return 2.0;
     35 
     36 	}
     37 
     38 /******************************************************************************/
     39 
     40 real64 dng_resample_bicubic::Evaluate (real64 x) const
     41 	{
     42 
     43 	const real64 A = -0.75;
     44 
     45 	x = Abs_real64 (x);
     46 
     47     if (x >= 2.0)
     48         return 0.0;
     49 
     50     else if (x >= 1.0)
     51         return (((A * x - 5.0 * A) * x + 8.0 * A) * x - 4.0 * A);
     52 
     53     else
     54         return (((A + 2.0) * x - (A + 3.0)) * x * x + 1.0);
     55 
     56 	}
     57 
     58 /******************************************************************************/
     59 
     60 const dng_resample_function & dng_resample_bicubic::Get ()
     61 	{
     62 
     63 	static dng_resample_bicubic static_dng_resample_bicubic;
     64 
     65 	return static_dng_resample_bicubic;
     66 
     67 	}
     68 
     69 /*****************************************************************************/
     70 
     71 dng_resample_coords::dng_resample_coords ()
     72 
     73 	:	fOrigin (0)
     74 	,	fCoords ()
     75 
     76 	{
     77 
     78 	}
     79 
     80 /*****************************************************************************/
     81 
     82 dng_resample_coords::~dng_resample_coords ()
     83 	{
     84 
     85 	}
     86 
     87 /*****************************************************************************/
     88 
     89 void dng_resample_coords::Initialize (int32 srcOrigin,
     90 									  int32 dstOrigin,
     91 									  uint32 srcCount,
     92 									  uint32 dstCount,
     93 									  dng_memory_allocator &allocator)
     94 	{
     95 
     96 	fOrigin = dstOrigin;
     97 
     98 	uint32 dstEntries = 0;
     99 	uint32 bufferSize = 0;
    100 	if (!RoundUpUint32ToMultiple(dstCount, 8, &dstEntries) ||
    101 	    !SafeUint32Mult(dstEntries, sizeof(int32), &bufferSize)) {
    102 		ThrowMemoryFull("Arithmetic overflow computing size for coordinate "
    103 						"buffer");
    104 	}
    105 	fCoords.Reset (allocator.Allocate (bufferSize));
    106 
    107 	int32 *coords = fCoords->Buffer_int32 ();
    108 
    109 	real64 invScale = (real64) srcCount /
    110 					  (real64) dstCount;
    111 
    112 	for (uint32 j = 0; j < dstCount; j++)
    113 		{
    114 
    115 		real64 x = (real64) j + 0.5;
    116 
    117 		real64 y = x * invScale - 0.5 + (real64) srcOrigin;
    118 
    119 		coords [j] = Round_int32 (y * (real64) kResampleSubsampleCount);
    120 
    121 		}
    122 
    123 	// Pad out table by replicating last entry.
    124 
    125 	for (uint32 k = dstCount; k < dstEntries; k++)
    126 		{
    127 
    128 		coords [k] = coords [dstCount - 1];
    129 
    130 		}
    131 
    132 	}
    133 
    134 /*****************************************************************************/
    135 
    136 dng_resample_weights::dng_resample_weights ()
    137 
    138 	:	fRadius (0)
    139 
    140 	,	fWeightStep (0)
    141 
    142 	,	fWeights32 ()
    143 	,	fWeights16 ()
    144 
    145 	{
    146 
    147 	}
    148 
    149 /*****************************************************************************/
    150 
    151 dng_resample_weights::~dng_resample_weights ()
    152 	{
    153 
    154 	}
    155 
    156 /*****************************************************************************/
    157 
    158 void dng_resample_weights::Initialize (real64 scale,
    159 									   const dng_resample_function &kernel,
    160 									   dng_memory_allocator &allocator)
    161 	{
    162 
    163 	uint32 j;
    164 
    165 	// We only adjust the kernel size for scale factors less than 1.0.
    166 
    167 	scale = Min_real64 (scale, 1.0);
    168 
    169 	// Find radius of this kernel.
    170 
    171 	fRadius = (uint32) (kernel.Extent () / scale + 0.9999);
    172 
    173 	// Width is twice the radius.
    174 
    175 	uint32 width = fRadius * 2;
    176 
    177 	// Round to each set to weights to a multiple of 8 entries.
    178 
    179 	if (!RoundUpUint32ToMultiple (width, 8, &fWeightStep))
    180 		{
    181 
    182 		ThrowMemoryFull ("Arithmetic overflow computing fWeightStep");
    183 
    184 		}
    185 
    186 	// Allocate and zero weight tables.
    187 
    188 	uint32 bufferSize = 0;
    189 
    190 	if (!SafeUint32Mult (fWeightStep, kResampleSubsampleCount, &bufferSize) ||
    191 		 !SafeUint32Mult (bufferSize, (uint32) sizeof (real32), &bufferSize))
    192 		{
    193 
    194 		ThrowMemoryFull("Arithmetic overflow computing buffer size.");
    195 
    196 		}
    197 
    198 	fWeights32.Reset (allocator.Allocate (bufferSize));
    199 
    200 	DoZeroBytes (fWeights32->Buffer		 (),
    201 				 fWeights32->LogicalSize ());
    202 
    203 	if (!SafeUint32Mult (fWeightStep, kResampleSubsampleCount, &bufferSize) ||
    204 		 !SafeUint32Mult (bufferSize, (uint32) sizeof (int16), &bufferSize))
    205 		{
    206 
    207 		ThrowMemoryFull("Arithmetic overflow computing buffer size.");
    208 
    209 		}
    210 
    211 	fWeights16.Reset (allocator.Allocate (bufferSize));
    212 
    213 	DoZeroBytes (fWeights16->Buffer		 (),
    214 				 fWeights16->LogicalSize ());
    215 
    216 	// Compute kernel for each subsample values.
    217 
    218 	for (uint32 sample = 0; sample < kResampleSubsampleCount; sample++)
    219 		{
    220 
    221 		real64 fract = sample * (1.0 / (real64) kResampleSubsampleCount);
    222 
    223 		real32 *w32 = fWeights32->Buffer_real32 () + fWeightStep * sample;
    224 
    225 		// Evaluate kernel function for 32 bit weights.
    226 
    227 			{
    228 
    229 			real64 t32 = 0.0;
    230 
    231 			for (j = 0; j < width; j++)
    232 				{
    233 
    234 				int32 k = (int32) j - (int32) fRadius + 1;
    235 
    236 				real64 x = (k - fract) * scale;
    237 
    238 				w32 [j] = (real32) kernel.Evaluate (x);
    239 
    240 				t32 += w32 [j];
    241 
    242 				}
    243 
    244 			// Scale 32 bit weights so total of weights is 1.0.
    245 
    246 			real32 s32 = (real32) (1.0 / t32);
    247 
    248 			for (j = 0; j < width; j++)
    249 				{
    250 
    251 				w32 [j] *= s32;
    252 
    253 				}
    254 
    255 			}
    256 
    257 		// Round off 32 bit weights to 16 bit weights.
    258 
    259 			{
    260 
    261 			int16 *w16 = fWeights16->Buffer_int16 () + fWeightStep * sample;
    262 
    263 			int32 t16 = 0;
    264 
    265 			for (j = 0; j < width; j++)
    266 				{
    267 
    268 				w16 [j] = (int16) Round_int32 (w32 [j] * 16384.0);
    269 
    270 				t16 += w16 [j];
    271 
    272 				}
    273 
    274 			// Adjust center entry for any round off error so total is
    275 			// exactly 16384.
    276 
    277 			w16 [fRadius - (fract >= 0.5 ? 0 : 1)] += (int16) (16384 - t16);
    278 
    279 			}
    280 
    281 		}
    282 
    283 	}
    284 
    285 /*****************************************************************************/
    286 
    287 dng_resample_weights_2d::dng_resample_weights_2d ()
    288 
    289 	:	fRadius (0)
    290 
    291 	,	fRowStep (0)
    292 	,	fColStep (0)
    293 
    294 	,	fWeights32 ()
    295 	,	fWeights16 ()
    296 
    297 	{
    298 
    299 	}
    300 
    301 /*****************************************************************************/
    302 
    303 dng_resample_weights_2d::~dng_resample_weights_2d ()
    304 	{
    305 
    306 	}
    307 
    308 /*****************************************************************************/
    309 
    310 void dng_resample_weights_2d::Initialize (const dng_resample_function &kernel,
    311 										  dng_memory_allocator &allocator)
    312 	{
    313 
    314 	// Find radius of this kernel. Unlike with 1d resample weights (see
    315 	// dng_resample_weights), we never scale up the kernel size.
    316 
    317 	fRadius = (uint32) (kernel.Extent () + 0.9999);
    318 
    319 	// Width is twice the radius.
    320 
    321 	uint32 width    = 0;
    322 	uint32 widthSqr = 0;
    323 	uint32 step = 0;
    324 
    325 	if (!SafeUint32Mult (fRadius, 2, &width) ||
    326 		 !SafeUint32Mult (width, width, &widthSqr) ||
    327 		 !RoundUpUint32ToMultiple (widthSqr, 8, &step) ||
    328 		 !SafeUint32Mult (step, kResampleSubsampleCount2D, &fRowStep))
    329 			{
    330 
    331 			ThrowMemoryFull ("Arithmetic overflow computing row step.");
    332 
    333 			}
    334 
    335 	fColStep = step;
    336 
    337 	// Allocate and zero weight tables.
    338 
    339 	uint32 bufferSize = 0;
    340 
    341 	if (!SafeUint32Mult (step, kResampleSubsampleCount2D, &bufferSize) ||
    342 		 !SafeUint32Mult (bufferSize, kResampleSubsampleCount2D, &bufferSize) ||
    343 		 !SafeUint32Mult (bufferSize, (uint32) sizeof (real32), &bufferSize))
    344 		{
    345 
    346 		ThrowMemoryFull ("Arithmetic overflow computing buffer size.");
    347 
    348 		}
    349 
    350 	fWeights32.Reset (allocator.Allocate (bufferSize));
    351 
    352 	DoZeroBytes (fWeights32->Buffer		 (),
    353 				 fWeights32->LogicalSize ());
    354 
    355 
    356 	if (!SafeUint32Mult (step, kResampleSubsampleCount2D, &bufferSize) ||
    357 		 !SafeUint32Mult (bufferSize, kResampleSubsampleCount2D, &bufferSize) ||
    358 		 !SafeUint32Mult (bufferSize, (uint32) sizeof (int16), &bufferSize))
    359 		{
    360 
    361 		ThrowMemoryFull ("Arithmetic overflow computing buffer size.");
    362 
    363 		}
    364 
    365 	fWeights16.Reset (allocator.Allocate (bufferSize));
    366 
    367 	DoZeroBytes (fWeights16->Buffer		 (),
    368 				 fWeights16->LogicalSize ());
    369 
    370 	// Compute kernel for each subsample values.
    371 
    372 	for (uint32 y = 0; y < kResampleSubsampleCount2D; y++)
    373 		{
    374 
    375 		real64 yFract = y * (1.0 / (real64) kResampleSubsampleCount2D);
    376 
    377 		for (uint32 x = 0; x < kResampleSubsampleCount2D; x++)
    378 			{
    379 
    380 			real64 xFract = x * (1.0 / (real64) kResampleSubsampleCount2D);
    381 
    382 			real32 *w32 = (real32 *) Weights32 (dng_point ((int32) y,
    383 														   (int32) x));
    384 
    385 			// Evaluate kernel function for 32 bit weights.
    386 
    387 				{
    388 
    389 				real64 t32 = 0.0;
    390 
    391 				uint32 index = 0;
    392 
    393 				for (uint32 i = 0; i < width; i++)
    394 					{
    395 
    396 					int32 yInt = ((int32) i) - (int32) fRadius + 1;
    397 					real64 yPos = yInt - yFract;
    398 
    399 					for (uint32 j = 0; j < width; j++)
    400 						{
    401 
    402 						int32 xInt = ((int32) j) - (int32) fRadius + 1;
    403 						real64 xPos = xInt - xFract;
    404 
    405 						#if 0
    406 
    407 						// Radial.
    408 
    409 						real64 dy2 = yPos * yPos;
    410 						real64 dx2 = xPos * xPos;
    411 
    412 						real64 r = sqrt (dx2 + dy2);
    413 
    414 						w32 [index] = (real32) kernel.Evaluate (r);
    415 
    416 						#else
    417 
    418 						// Separable.
    419 
    420 						w32 [index] = (real32) kernel.Evaluate (xPos) *
    421 							          (real32) kernel.Evaluate (yPos);
    422 
    423 						#endif
    424 
    425 						t32 += w32 [index];
    426 
    427 						index++;
    428 
    429 						}
    430 
    431 					}
    432 
    433 				// Scale 32 bit weights so total of weights is 1.0.
    434 
    435 				const real32 s32 = (real32) (1.0 / t32);
    436 
    437 				for (uint32 i = 0; i < widthSqr; i++)
    438 					{
    439 
    440 					w32 [i] *= s32;
    441 
    442 					}
    443 
    444 				}
    445 
    446 			// Round off 32 bit weights to 16 bit weights.
    447 
    448 				{
    449 
    450 				int16 *w16 = (int16 *) Weights16 (dng_point ((int32) y,
    451 															 (int32) x));
    452 
    453 				int32 t16 = 0;
    454 
    455 				for (uint32 j = 0; j < widthSqr; j++)
    456 					{
    457 
    458 					w16 [j] = (int16) Round_int32 (w32 [j] * 16384.0);
    459 
    460 					t16 += w16 [j];
    461 
    462 					}
    463 
    464 				// Adjust one of the center entries for any round off error so total
    465 				// is exactly 16384.
    466 
    467 				const uint32 xOffset      = fRadius - ((xFract >= 0.5) ? 0 : 1);
    468 				const uint32 yOffset      = fRadius - ((yFract >= 0.5) ? 0 : 1);
    469 				const uint32 centerOffset = width * yOffset + xOffset;
    470 
    471 				w16 [centerOffset] += (int16) (16384 - t16);
    472 
    473 				}
    474 
    475 			}
    476 
    477 		}
    478 
    479 	}
    480 
    481 /*****************************************************************************/
    482 
    483 class dng_resample_task: public dng_filter_task
    484 	{
    485 
    486 	protected:
    487 
    488 		dng_rect fSrcBounds;
    489 		dng_rect fDstBounds;
    490 
    491 		const dng_resample_function &fKernel;
    492 
    493 		real64 fRowScale;
    494 		real64 fColScale;
    495 
    496 		dng_resample_coords fRowCoords;
    497 		dng_resample_coords fColCoords;
    498 
    499 		dng_resample_weights fWeightsV;
    500 		dng_resample_weights fWeightsH;
    501 
    502 		dng_point fSrcTileSize;
    503 
    504 		AutoPtr<dng_memory_block> fTempBuffer [kMaxMPThreads];
    505 
    506 	public:
    507 
    508 		dng_resample_task (const dng_image &srcImage,
    509 						   dng_image &dstImage,
    510 						   const dng_rect &srcBounds,
    511 						   const dng_rect &dstBounds,
    512 						   const dng_resample_function &kernel);
    513 
    514 		virtual dng_rect SrcArea (const dng_rect &dstArea);
    515 
    516 		virtual dng_point SrcTileSize (const dng_point &dstTileSize);
    517 
    518 		virtual void Start (uint32 threadCount,
    519 							const dng_point &tileSize,
    520 							dng_memory_allocator *allocator,
    521 							dng_abort_sniffer *sniffer);
    522 
    523 		virtual void ProcessArea (uint32 threadIndex,
    524 								  dng_pixel_buffer &srcBuffer,
    525 								  dng_pixel_buffer &dstBuffer);
    526 
    527 	};
    528 
    529 /*****************************************************************************/
    530 
    531 dng_resample_task::dng_resample_task (const dng_image &srcImage,
    532 						   			  dng_image &dstImage,
    533 						   			  const dng_rect &srcBounds,
    534 						   			  const dng_rect &dstBounds,
    535 									  const dng_resample_function &kernel)
    536 
    537 	:	dng_filter_task (srcImage,
    538 						 dstImage)
    539 
    540 	,	fSrcBounds (srcBounds)
    541 	,	fDstBounds (dstBounds)
    542 
    543 	,	fKernel (kernel)
    544 
    545 	,	fRowScale ((srcBounds.H () != 0) ? dstBounds.H () / (real64) srcBounds.H () : 0)
    546 	,	fColScale ((srcBounds.W () != 0) ? dstBounds.W () / (real64) srcBounds.W () : 0)
    547 
    548 	,	fRowCoords ()
    549 	,	fColCoords ()
    550 
    551 	,	fWeightsV ()
    552 	,	fWeightsH ()
    553 
    554 	,	fSrcTileSize ()
    555 
    556 	{
    557 	if (fRowScale == 0 || fColScale == 0)
    558 		{
    559 		 ThrowBadFormat ();
    560 	}
    561 
    562 	if (srcImage.PixelSize  () <= 2 &&
    563 		dstImage.PixelSize  () <= 2 &&
    564 		srcImage.PixelRange () == dstImage.PixelRange ())
    565 		{
    566 		fSrcPixelType = ttShort;
    567 		fDstPixelType = ttShort;
    568 		}
    569 
    570 	else
    571 		{
    572 		fSrcPixelType = ttFloat;
    573 		fDstPixelType = ttFloat;
    574 		}
    575 
    576 	fUnitCell = dng_point (8, 8);
    577 
    578 	fMaxTileSize.v = Pin_int32 (fUnitCell.v,
    579 								Round_int32 (fMaxTileSize.v * fRowScale),
    580 								fMaxTileSize.v);
    581 
    582 	fMaxTileSize.h = Pin_int32 (fUnitCell.h,
    583 								Round_int32 (fMaxTileSize.h * fColScale),
    584 								fMaxTileSize.h);
    585 
    586 	}
    587 
    588 /*****************************************************************************/
    589 
    590 dng_rect dng_resample_task::SrcArea (const dng_rect &dstArea)
    591 	{
    592 
    593 	int32 offsetV = fWeightsV.Offset ();
    594 	int32 offsetH = fWeightsH.Offset ();
    595 
    596 	uint32 widthV = fWeightsV.Width ();
    597 	uint32 widthH = fWeightsH.Width ();
    598 
    599 	dng_rect srcArea;
    600 
    601 	srcArea.t = SafeInt32Add (fRowCoords.Pixel (dstArea.t), offsetV);
    602 	srcArea.l = SafeInt32Add (fColCoords.Pixel (dstArea.l), offsetH);
    603 
    604 	srcArea.b = SafeInt32Add (SafeInt32Add (
    605 						fRowCoords.Pixel (SafeInt32Sub (dstArea.b, 1)),
    606 						offsetV),
    607 					ConvertUint32ToInt32 (widthV));;
    608 	srcArea.r = SafeInt32Add(SafeInt32Add(
    609 						fColCoords.Pixel (SafeInt32Sub (dstArea.r, 1)),
    610 						offsetH),
    611 					ConvertUint32ToInt32(widthH));;
    612 
    613 	return srcArea;
    614 
    615 	}
    616 
    617 /*****************************************************************************/
    618 
    619 dng_point dng_resample_task::SrcTileSize (const dng_point & /* dstTileSize */)
    620 	{
    621 
    622 	return fSrcTileSize;
    623 
    624 	}
    625 
    626 /*****************************************************************************/
    627 
    628 void dng_resample_task::Start (uint32 threadCount,
    629 							   const dng_point &tileSize,
    630 							   dng_memory_allocator *allocator,
    631 							   dng_abort_sniffer *sniffer)
    632 	{
    633 
    634 	// Compute sub-pixel resolution coordinates in the source image for
    635 	// each row and column of the destination area.
    636 
    637 	fRowCoords.Initialize (fSrcBounds.t,
    638 						   fDstBounds.t,
    639 						   fSrcBounds.H (),
    640 						   fDstBounds.H (),
    641 						   *allocator);
    642 
    643 	fColCoords.Initialize (fSrcBounds.l,
    644 						   fDstBounds.l,
    645 						   fSrcBounds.W (),
    646 						   fDstBounds.W (),
    647 						   *allocator);
    648 
    649 	// Compute resampling kernels.
    650 
    651 	fWeightsV.Initialize (fRowScale,
    652 						  fKernel,
    653 						  *allocator);
    654 
    655 	fWeightsH.Initialize (fColScale,
    656 						  fKernel,
    657 						  *allocator);
    658 
    659 	// Find upper bound on source source tile.
    660 
    661 	fSrcTileSize.v = Round_int32 (tileSize.v / fRowScale) + fWeightsV.Width () + 2;
    662 	fSrcTileSize.h = Round_int32 (tileSize.h / fColScale) + fWeightsH.Width () + 2;
    663 
    664 	// Allocate temp buffers.
    665 
    666 	uint32 tempBufferSize = 0;
    667 	if (!RoundUpUint32ToMultiple (fSrcTileSize.h, 8, &tempBufferSize) ||
    668 		 !SafeUint32Mult (tempBufferSize,
    669 			 static_cast<uint32> (sizeof (real32)),
    670 			 &tempBufferSize))
    671 		{
    672 
    673 		ThrowMemoryFull("Arithmetic overflow computing buffer size.");
    674 
    675 		}
    676 
    677 	for (uint32 threadIndex = 0; threadIndex < threadCount; threadIndex++)
    678 		{
    679 
    680 		fTempBuffer [threadIndex] . Reset (allocator->Allocate (tempBufferSize));
    681 
    682 		}
    683 
    684 	// Allocate the pixel buffers.
    685 
    686 	dng_filter_task::Start (threadCount,
    687 							tileSize,
    688 							allocator,
    689 							sniffer);
    690 
    691 	}
    692 
    693 /*****************************************************************************/
    694 
    695 void dng_resample_task::ProcessArea (uint32 threadIndex,
    696 								     dng_pixel_buffer &srcBuffer,
    697 								     dng_pixel_buffer &dstBuffer)
    698 	{
    699 
    700 	dng_rect srcArea = srcBuffer.fArea;
    701 	dng_rect dstArea = dstBuffer.fArea;
    702 
    703 	uint32 srcCols = srcArea.W ();
    704 	uint32 dstCols = dstArea.W ();
    705 
    706 	uint32 widthV = fWeightsV.Width ();
    707 	uint32 widthH = fWeightsH.Width ();
    708 
    709 	int32 offsetV = fWeightsV.Offset ();
    710 	int32 offsetH = fWeightsH.Offset ();
    711 
    712 	uint32 stepH = fWeightsH.Step ();
    713 
    714 	const int32 *rowCoords = fRowCoords.Coords (0        );
    715 	const int32 *colCoords = fColCoords.Coords (dstArea.l);
    716 
    717 	if (fSrcPixelType == ttFloat)
    718 		{
    719 
    720 		const real32 *weightsH = fWeightsH.Weights32 (0);
    721 
    722 		real32 *tPtr = fTempBuffer [threadIndex]->Buffer_real32 ();
    723 
    724 		real32 *ttPtr = tPtr + offsetH - srcArea.l;
    725 
    726 		for (int32 dstRow = dstArea.t; dstRow < dstArea.b; dstRow++)
    727 			{
    728 
    729 			int32 rowCoord = rowCoords [dstRow];
    730 
    731 			int32 rowFract = rowCoord & kResampleSubsampleMask;
    732 
    733 			const real32 *weightsV = fWeightsV.Weights32 (rowFract);
    734 
    735 			int32 srcRow = (rowCoord >> kResampleSubsampleBits) + offsetV;
    736 
    737 			for (uint32 plane = 0; plane < dstBuffer.fPlanes; plane++)
    738 				{
    739 
    740 				const real32 *sPtr = srcBuffer.ConstPixel_real32 (srcRow,
    741 																  srcArea.l,
    742 																  plane);
    743 
    744 				DoResampleDown32 (sPtr,
    745 								  tPtr,
    746 								  srcCols,
    747 								  srcBuffer.fRowStep,
    748 								  weightsV,
    749 								  widthV);
    750 
    751 				real32 *dPtr = dstBuffer.DirtyPixel_real32 (dstRow,
    752 															dstArea.l,
    753 															plane);
    754 
    755 				DoResampleAcross32 (ttPtr,
    756 									dPtr,
    757 									dstCols,
    758 									colCoords,
    759 									weightsH,
    760 									widthH,
    761 									stepH);
    762 
    763 				}
    764 
    765 			}
    766 
    767 		}
    768 
    769 	else
    770 		{
    771 
    772 		const int16 *weightsH = fWeightsH.Weights16 (0);
    773 
    774 		uint16 *tPtr = fTempBuffer [threadIndex]->Buffer_uint16 ();
    775 
    776 		uint16 *ttPtr = tPtr + offsetH - srcArea.l;
    777 
    778 		uint32 pixelRange = fDstImage.PixelRange ();
    779 
    780 		for (int32 dstRow = dstArea.t; dstRow < dstArea.b; dstRow++)
    781 			{
    782 
    783 			int32 rowCoord = rowCoords [dstRow];
    784 
    785 			int32 rowFract = rowCoord & kResampleSubsampleMask;
    786 
    787 			const int16 *weightsV = fWeightsV.Weights16 (rowFract);
    788 
    789 			int32 srcRow = (rowCoord >> kResampleSubsampleBits) + offsetV;
    790 
    791 			for (uint32 plane = 0; plane < dstBuffer.fPlanes; plane++)
    792 				{
    793 
    794 				const uint16 *sPtr = srcBuffer.ConstPixel_uint16 (srcRow,
    795 																  srcArea.l,
    796 																  plane);
    797 
    798 				DoResampleDown16 (sPtr,
    799 								  tPtr,
    800 								  srcCols,
    801 								  srcBuffer.fRowStep,
    802 								  weightsV,
    803 								  widthV,
    804 								  pixelRange);
    805 
    806 				uint16 *dPtr = dstBuffer.DirtyPixel_uint16 (dstRow,
    807 															dstArea.l,
    808 															plane);
    809 
    810 				DoResampleAcross16 (ttPtr,
    811 									dPtr,
    812 									dstCols,
    813 									colCoords,
    814 									weightsH,
    815 									widthH,
    816 									stepH,
    817 									pixelRange);
    818 
    819 				}
    820 
    821 			}
    822 
    823 		}
    824 
    825 	}
    826 
    827 /*****************************************************************************/
    828 
    829 void ResampleImage (dng_host &host,
    830 					const dng_image &srcImage,
    831 					dng_image &dstImage,
    832 					const dng_rect &srcBounds,
    833 					const dng_rect &dstBounds,
    834 					const dng_resample_function &kernel)
    835 	{
    836 
    837 	dng_resample_task task (srcImage,
    838 							dstImage,
    839 							srcBounds,
    840 							dstBounds,
    841 							kernel);
    842 
    843 	host.PerformAreaTask (task,
    844 						  dstBounds);
    845 
    846 	}
    847 
    848 /*****************************************************************************/
    849