File indexing completed on 2025-01-19 03:55:15

0001 /*****************************************************************************/
0002 // Copyright 2006-2019 Adobe Systems Incorporated
0003 // All Rights Reserved.
0004 //
0005 // NOTICE:  Adobe permits you to use, modify, and distribute this file in
0006 // accordance with the terms of the Adobe license agreement accompanying it.
0007 /*****************************************************************************/
0008 
0009 #include "dng_resample.h"
0010 
0011 #include "dng_assertions.h"
0012 #include "dng_bottlenecks.h"
0013 #include "dng_filter_task.h"
0014 #include "dng_host.h"
0015 #include "dng_image.h"
0016 #include "dng_memory.h"
0017 #include "dng_pixel_buffer.h"
0018 #include "dng_safe_arithmetic.h"
0019 #include "dng_tag_types.h"
0020 #include "dng_utils.h"
0021 
0022 /******************************************************************************/
0023 
0024 real64 dng_resample_bicubic::Extent () const
0025     {
0026 
0027     return 2.0;
0028 
0029     }
0030 
0031 /******************************************************************************/
0032 
0033 real64 dng_resample_bicubic::Evaluate (real64 x) const
0034     {
0035 
0036     const real64 A = -0.75;
0037 
0038     x = Abs_real64 (x);
0039 
0040     if (x >= 2.0)
0041         return 0.0;
0042 
0043     else if (x >= 1.0)
0044         return (((A * x - 5.0 * A) * x + 8.0 * A) * x - 4.0 * A);
0045 
0046     else
0047         return (((A + 2.0) * x - (A + 3.0)) * x * x + 1.0);
0048 
0049     }
0050 
0051 /******************************************************************************/
0052 
0053 const dng_resample_function & dng_resample_bicubic::Get ()
0054     {
0055 
0056     static dng_resample_bicubic static_dng_resample_bicubic;
0057 
0058     return static_dng_resample_bicubic;
0059 
0060     }
0061 
0062 /*****************************************************************************/
0063 
0064 dng_resample_coords::dng_resample_coords ()
0065 
0066     :   fOrigin (0)
0067     ,   fCoords ()
0068 
0069     {
0070 
0071     }
0072 
0073 /*****************************************************************************/
0074 
0075 dng_resample_coords::~dng_resample_coords ()
0076     {
0077 
0078     }
0079 
0080 /*****************************************************************************/
0081 
0082 void dng_resample_coords::Initialize (int32 srcOrigin,
0083                                       int32 dstOrigin,
0084                                       uint32 srcCount,
0085                                       uint32 dstCount,
0086                                       dng_memory_allocator &allocator)
0087     {
0088 
0089     fOrigin = dstOrigin;
0090 
0091     uint32 dstEntries = 0;
0092     uint32 bufferSize = 0;
0093 
0094     if (!RoundUpUint32ToMultiple (dstCount, 8, &dstEntries) ||
0095         !SafeUint32Mult (dstEntries, sizeof (int32), &bufferSize))
0096         {
0097 
0098         ThrowOverflow ("Arithmetic overflow computing size for coordinate "
0099                        "buffer");
0100 
0101         }
0102 
0103     fCoords.Reset (allocator.Allocate (bufferSize));
0104 
0105     int32 *coords = fCoords->Buffer_int32 ();
0106 
0107     real64 invScale = (real64) srcCount /
0108                       (real64) dstCount;
0109 
0110     for (uint32 j = 0; j < dstCount; j++)
0111         {
0112 
0113         real64 x = (real64) j + 0.5;
0114 
0115         real64 y = x * invScale - 0.5 + (real64) srcOrigin;
0116 
0117         coords [j] = Round_int32 (y * (real64) kResampleSubsampleCount);
0118 
0119         }
0120 
0121     // Pad out table by replicating last entry.
0122 
0123     for (uint32 k = dstCount; k < dstEntries; k++)
0124         {
0125 
0126         coords [k] = coords [dstCount - 1];
0127 
0128         }
0129 
0130     }
0131 
0132 /*****************************************************************************/
0133 
0134 dng_resample_weights::dng_resample_weights ()
0135 
0136     :   fRadius (0)
0137 
0138     ,   fWeightStep (0)
0139 
0140     ,   fWeights32 ()
0141     ,   fWeights16 ()
0142 
0143     {
0144 
0145     }
0146 
0147 /*****************************************************************************/
0148 
0149 dng_resample_weights::~dng_resample_weights ()
0150     {
0151 
0152     }
0153 
0154 /*****************************************************************************/
0155 
0156 void dng_resample_weights::Initialize (real64 scale,
0157                                        const dng_resample_function &kernel,
0158                                        dng_memory_allocator &allocator)
0159     {
0160 
0161     uint32 j;
0162 
0163     // We only adjust the kernel size for scale factors less than 1.0.
0164 
0165     scale = Min_real64 (scale, 1.0);
0166 
0167     // Find radius of this kernel.
0168 
0169     fRadius = (uint32) (kernel.Extent () / scale + 0.9999);
0170 
0171     // Width is twice the radius.
0172 
0173     uint32 width = fRadius * 2;
0174 
0175     // Round to each set to weights to a multiple of 8 entries.
0176 
0177     if (!RoundUpUint32ToMultiple (width, 8, &fWeightStep))
0178         {
0179 
0180         ThrowOverflow ("Arithmetic overflow computing fWeightStep");
0181 
0182         }
0183 
0184     // Allocate and zero weight tables.
0185 
0186     uint32 bufferSize = 0;
0187 
0188     if (!SafeUint32Mult (fWeightStep, kResampleSubsampleCount, &bufferSize) ||
0189         !SafeUint32Mult (bufferSize, (uint32) sizeof (real32), &bufferSize))
0190         {
0191 
0192         ThrowOverflow ("Arithmetic overflow computing buffer size.");
0193 
0194         }
0195 
0196     fWeights32.Reset (allocator.Allocate (bufferSize));
0197 
0198     DoZeroBytes (fWeights32->Buffer      (),
0199                  fWeights32->LogicalSize ());
0200 
0201     if (!SafeUint32Mult (fWeightStep, kResampleSubsampleCount, &bufferSize) ||
0202         !SafeUint32Mult (bufferSize, (uint32) sizeof (int16), &bufferSize))
0203         {
0204 
0205         ThrowOverflow ("Arithmetic overflow computing buffer size.");
0206 
0207         }
0208 
0209     fWeights16.Reset (allocator.Allocate (bufferSize));
0210 
0211     DoZeroBytes (fWeights16->Buffer      (),
0212                  fWeights16->LogicalSize ());
0213 
0214     // Compute kernel for each subsample values.
0215 
0216     for (uint32 sample = 0; sample < kResampleSubsampleCount; sample++)
0217         {
0218 
0219         real64 fract = sample * (1.0 / (real64) kResampleSubsampleCount);
0220 
0221         real32 *w32 = fWeights32->Buffer_real32 () + fWeightStep * sample;
0222 
0223         // Evaluate kernel function for 32 bit weights.
0224 
0225             {
0226 
0227             real64 t32 = 0.0;
0228 
0229             for (j = 0; j < width; j++)
0230                 {
0231 
0232                 int32 k = (int32) j - (int32) fRadius + 1;
0233 
0234                 real64 x = (k - fract) * scale;
0235 
0236                 w32 [j] = (real32) kernel.Evaluate (x);
0237 
0238                 t32 += w32 [j];
0239 
0240                 }
0241 
0242             // Scale 32 bit weights so total of weights is 1.0.
0243 
0244             real32 s32 = (real32) (1.0 / t32);
0245 
0246             for (j = 0; j < width; j++)
0247                 {
0248 
0249                 w32 [j] *= s32;
0250 
0251                 }
0252 
0253             }
0254 
0255         // Round off 32 bit weights to 16 bit weights.
0256 
0257             {
0258 
0259             int16 *w16 = fWeights16->Buffer_int16 () + fWeightStep * sample;
0260 
0261             int32 t16 = 0;
0262 
0263             for (j = 0; j < width; j++)
0264                 {
0265 
0266                 w16 [j] = (int16) Round_int32 (w32 [j] * 16384.0);
0267 
0268                 t16 += w16 [j];
0269 
0270                 }
0271 
0272             // Adjust center entry for any round off error so total is
0273             // exactly 16384.
0274 
0275             w16 [fRadius - (fract >= 0.5 ? 0 : 1)] += (int16) (16384 - t16);
0276 
0277             }
0278 
0279         }
0280 
0281     }
0282 
0283 /*****************************************************************************/
0284 
0285 dng_resample_weights_2d::dng_resample_weights_2d ()
0286 
0287     :   fRadius (0)
0288 
0289     ,   fRowStep (0)
0290     ,   fColStep (0)
0291 
0292     ,   fWeights32 ()
0293     ,   fWeights16 ()
0294 
0295     {
0296 
0297     }
0298 
0299 /*****************************************************************************/
0300 
0301 dng_resample_weights_2d::~dng_resample_weights_2d ()
0302     {
0303 
0304     }
0305 
0306 /*****************************************************************************/
0307 
0308 void dng_resample_weights_2d::Initialize (const dng_resample_function &kernel,
0309                                           dng_memory_allocator &allocator)
0310     {
0311 
0312     // Find radius of this kernel. Unlike with 1d resample weights (see
0313     // dng_resample_weights), we never scale up the kernel size.
0314 
0315     fRadius = (uint32) (kernel.Extent () + 0.9999);
0316 
0317     // Width is twice the radius.
0318 
0319     uint32 width    = 0;
0320     uint32 widthSqr = 0;
0321     uint32 step     = 0;
0322 
0323     if (!SafeUint32Mult (fRadius, 2, &width) ||
0324         !SafeUint32Mult (width, width, &widthSqr) ||
0325         !RoundUpUint32ToMultiple (widthSqr, 8, &step) ||
0326         !SafeUint32Mult (step, kResampleSubsampleCount2D, &fRowStep))
0327         {
0328 
0329         ThrowOverflow ("Arithmetic overflow computing row step.");
0330 
0331         }
0332 
0333     fColStep = step;
0334 
0335     // Allocate and zero weight tables.
0336 
0337     uint32 bufferSize = 0;
0338 
0339     if (!SafeUint32Mult (step, kResampleSubsampleCount2D, &bufferSize) ||
0340         !SafeUint32Mult (bufferSize, kResampleSubsampleCount2D, &bufferSize) ||
0341         !SafeUint32Mult (bufferSize, (uint32) sizeof (real32), &bufferSize))
0342         {
0343 
0344         ThrowOverflow ("Arithmetic overflow computing buffer size.");
0345 
0346         }
0347 
0348     fWeights32.Reset (allocator.Allocate (bufferSize));
0349 
0350     DoZeroBytes (fWeights32->Buffer      (),
0351                  fWeights32->LogicalSize ());
0352 
0353     if (!SafeUint32Mult (step, kResampleSubsampleCount2D, &bufferSize) ||
0354         !SafeUint32Mult (bufferSize, kResampleSubsampleCount2D, &bufferSize) ||
0355         !SafeUint32Mult (bufferSize, (uint32) sizeof (int16), &bufferSize))
0356         {
0357 
0358         ThrowOverflow ("Arithmetic overflow computing buffer size.");
0359 
0360         }
0361 
0362     fWeights16.Reset (allocator.Allocate (bufferSize));
0363 
0364     DoZeroBytes (fWeights16->Buffer      (),
0365                  fWeights16->LogicalSize ());
0366 
0367     // Compute kernel for each subsample values.
0368 
0369     for (uint32 y = 0; y < kResampleSubsampleCount2D; y++)
0370         {
0371 
0372         real64 yFract = y * (1.0 / (real64) kResampleSubsampleCount2D);
0373 
0374         for (uint32 x = 0; x < kResampleSubsampleCount2D; x++)
0375             {
0376 
0377             real64 xFract = x * (1.0 / (real64) kResampleSubsampleCount2D);
0378 
0379             real32 *w32 = (real32 *) Weights32 (dng_point ((int32) y,
0380                                                            (int32) x));
0381 
0382             // Evaluate kernel function for 32 bit weights.
0383 
0384                 {
0385 
0386                 real64 t32 = 0.0;
0387 
0388                 uint32 index = 0;
0389 
0390                 for (uint32 i = 0; i < width; i++)
0391                     {
0392 
0393                     int32 yInt = ((int32) i) - (int32) fRadius + 1;
0394                     real64 yPos = yInt - yFract;
0395 
0396                     for (uint32 j = 0; j < width; j++)
0397                         {
0398 
0399                         int32 xInt = ((int32) j) - (int32) fRadius + 1;
0400                         real64 xPos = xInt - xFract;
0401 
0402                         #if 0
0403 
0404                         // Radial.
0405 
0406                         real64 dy2 = yPos * yPos;
0407                         real64 dx2 = xPos * xPos;
0408 
0409                         real64 r = sqrt (dx2 + dy2);
0410 
0411                         w32 [index] = (real32) kernel.Evaluate (r);
0412 
0413                         #else
0414 
0415                         // Separable.
0416 
0417                         w32 [index] = (real32) kernel.Evaluate (xPos) *
0418                                       (real32) kernel.Evaluate (yPos);
0419 
0420                         #endif
0421 
0422                         t32 += w32 [index];
0423 
0424                         index++;
0425 
0426                         }
0427 
0428                     }
0429 
0430                 // Scale 32 bit weights so total of weights is 1.0.
0431 
0432                 const real32 s32 = (real32) (1.0 / t32);
0433 
0434                 for (uint32 i = 0; i < widthSqr; i++)
0435                     {
0436 
0437                     w32 [i] *= s32;
0438 
0439                     }
0440 
0441                 }
0442 
0443             // Round off 32 bit weights to 16 bit weights.
0444 
0445                 {
0446 
0447                 int16 *w16 = (int16 *) Weights16 (dng_point ((int32) y,
0448                                                              (int32) x));
0449 
0450                 int32 t16 = 0;
0451 
0452                 for (uint32 j = 0; j < widthSqr; j++)
0453                     {
0454 
0455                     w16 [j] = (int16) Round_int32 (w32 [j] * 16384.0);
0456 
0457                     t16 += w16 [j];
0458 
0459                     }
0460 
0461                 // Adjust one of the center entries for any round off error so total
0462                 // is exactly 16384.
0463 
0464                 const uint32 xOffset      = fRadius - ((xFract >= 0.5) ? 0 : 1);
0465                 const uint32 yOffset      = fRadius - ((yFract >= 0.5) ? 0 : 1);
0466                 const uint32 centerOffset = width * yOffset + xOffset;
0467 
0468                 w16 [centerOffset] += (int16) (16384 - t16);
0469 
0470                 }
0471 
0472             }
0473 
0474         }
0475 
0476     }
0477 
0478 /*****************************************************************************/
0479 
0480 class dng_resample_task: public dng_filter_task
0481     {
0482 
0483     protected:
0484 
0485         dng_rect fSrcBounds;
0486         dng_rect fDstBounds;
0487 
0488         const dng_resample_function &fKernel;
0489 
0490         real64 fRowScale;
0491         real64 fColScale;
0492 
0493         dng_resample_coords fRowCoords;
0494         dng_resample_coords fColCoords;
0495 
0496         dng_resample_weights fWeightsV;
0497         dng_resample_weights fWeightsH;
0498 
0499         dng_point fSrcTileSize;
0500 
0501         AutoPtr<dng_memory_block> fTempBuffer [kMaxMPThreads];
0502 
0503     public:
0504 
0505         dng_resample_task (const dng_image &srcImage,
0506                            dng_image &dstImage,
0507                            const dng_rect &srcBounds,
0508                            const dng_rect &dstBounds,
0509                            const dng_resample_function &kernel);
0510 
0511         virtual dng_rect SrcArea (const dng_rect &dstArea);
0512 
0513         virtual dng_point SrcTileSize (const dng_point &dstTileSize);
0514 
0515         virtual void Start (uint32 threadCount,
0516                             const dng_rect &dstArea,
0517                             const dng_point &tileSize,
0518                             dng_memory_allocator *allocator,
0519                             dng_abort_sniffer *sniffer);
0520 
0521         virtual void ProcessArea (uint32 threadIndex,
0522                                   dng_pixel_buffer &srcBuffer,
0523                                   dng_pixel_buffer &dstBuffer);
0524 
0525     };
0526 
0527 /*****************************************************************************/
0528 
0529 dng_resample_task::dng_resample_task (const dng_image &srcImage,
0530                                       dng_image &dstImage,
0531                                       const dng_rect &srcBounds,
0532                                       const dng_rect &dstBounds,
0533                                       const dng_resample_function &kernel)
0534 
0535     :   dng_filter_task ("dng_resample_task",
0536                          srcImage,
0537                          dstImage)
0538 
0539     ,   fSrcBounds (srcBounds)
0540     ,   fDstBounds (dstBounds)
0541 
0542     ,   fKernel (kernel)
0543 
0544     ,   fRowScale (dstBounds.H () / (real64) srcBounds.H ())
0545     ,   fColScale (dstBounds.W () / (real64) srcBounds.W ())
0546 
0547     ,   fRowCoords ()
0548     ,   fColCoords ()
0549 
0550     ,   fWeightsV ()
0551     ,   fWeightsH ()
0552 
0553     ,   fSrcTileSize ()
0554 
0555     {
0556 
0557     if (srcImage.PixelSize  () <= 2 &&
0558         dstImage.PixelSize  () <= 2 &&
0559         srcImage.PixelRange () == dstImage.PixelRange ())
0560         {
0561         fSrcPixelType = ttShort;
0562         fDstPixelType = ttShort;
0563         }
0564 
0565     else
0566         {
0567         fSrcPixelType = ttFloat;
0568         fDstPixelType = ttFloat;
0569         }
0570 
0571     fUnitCell = dng_point (8, 8);
0572 
0573     fMaxTileSize.v = Pin_int32 (fUnitCell.v,
0574                                 Round_int32 (fMaxTileSize.v * fRowScale),
0575                                 fMaxTileSize.v);
0576 
0577     fMaxTileSize.h = Pin_int32 (fUnitCell.h,
0578                                 Round_int32 (fMaxTileSize.h * fColScale),
0579                                 fMaxTileSize.h);
0580 
0581     }
0582 
0583 /*****************************************************************************/
0584 
0585 DNG_ATTRIB_NO_SANITIZE("unsigned-integer-overflow")
0586 dng_rect dng_resample_task::SrcArea (const dng_rect &dstArea)
0587     {
0588 
0589     int32 offsetV = fWeightsV.Offset ();
0590     int32 offsetH = fWeightsH.Offset ();
0591 
0592     uint32 widthV = fWeightsV.Width ();
0593     uint32 widthH = fWeightsH.Width ();
0594 
0595     dng_rect srcArea;
0596 
0597     srcArea.t = fRowCoords.Pixel (dstArea.t) + offsetV;
0598     srcArea.l = fColCoords.Pixel (dstArea.l) + offsetH;
0599 
0600     srcArea.b = fRowCoords.Pixel (dstArea.b - 1) + offsetV + widthV;
0601     srcArea.r = fColCoords.Pixel (dstArea.r - 1) + offsetH + widthH;
0602 
0603     return srcArea;
0604 
0605     }
0606 
0607 /*****************************************************************************/
0608 
0609 dng_point dng_resample_task::SrcTileSize (const dng_point & /* dstTileSize */)
0610     {
0611 
0612     return fSrcTileSize;
0613 
0614     }
0615 
0616 /*****************************************************************************/
0617 
0618 void dng_resample_task::Start (uint32 threadCount,
0619                                const dng_rect &dstArea,
0620                                const dng_point &tileSize,
0621                                dng_memory_allocator *allocator,
0622                                dng_abort_sniffer *sniffer)
0623     {
0624 
0625     // Compute sub-pixel resolution coordinates in the source image for
0626     // each row and column of the destination area.
0627 
0628     fRowCoords.Initialize (fSrcBounds.t,
0629                            fDstBounds.t,
0630                            fSrcBounds.H (),
0631                            fDstBounds.H (),
0632                            *allocator);
0633 
0634     fColCoords.Initialize (fSrcBounds.l,
0635                            fDstBounds.l,
0636                            fSrcBounds.W (),
0637                            fDstBounds.W (),
0638                            *allocator);
0639 
0640     // Compute resampling kernels.
0641 
0642     fWeightsV.Initialize (fRowScale,
0643                           fKernel,
0644                           *allocator);
0645 
0646     fWeightsH.Initialize (fColScale,
0647                           fKernel,
0648                           *allocator);
0649 
0650     // Find upper bound on source source tile.
0651 
0652     fSrcTileSize.v = Round_int32 (tileSize.v / fRowScale) + fWeightsV.Width () + 2;
0653     fSrcTileSize.h = Round_int32 (tileSize.h / fColScale) + fWeightsH.Width () + 2;
0654 
0655     // Allocate temp buffers.
0656 
0657     uint32 tempBufferSize = 0;
0658 
0659     if (!RoundUpUint32ToMultiple (fSrcTileSize.h, 8, &tempBufferSize) ||
0660         !SafeUint32Mult (tempBufferSize,
0661                          static_cast<uint32> (sizeof (real32)),
0662                          &tempBufferSize))
0663         {
0664 
0665         ThrowOverflow ("Arithmetic overflow computing buffer size.");
0666 
0667         }
0668 
0669     for (uint32 threadIndex = 0; threadIndex < threadCount; threadIndex++)
0670         {
0671 
0672         fTempBuffer [threadIndex] . Reset (allocator->Allocate (tempBufferSize));
0673 
0674         }
0675 
0676     // Allocate the pixel buffers.
0677 
0678     dng_filter_task::Start (threadCount,
0679                             dstArea,
0680                             tileSize,
0681                             allocator,
0682                             sniffer);
0683 
0684     }
0685 
0686 /*****************************************************************************/
0687 
0688 void dng_resample_task::ProcessArea (uint32 threadIndex,
0689                                      dng_pixel_buffer &srcBuffer,
0690                                      dng_pixel_buffer &dstBuffer)
0691     {
0692 
0693     dng_rect srcArea = srcBuffer.fArea;
0694     dng_rect dstArea = dstBuffer.fArea;
0695 
0696     uint32 srcCols = srcArea.W ();
0697     uint32 dstCols = dstArea.W ();
0698 
0699     uint32 widthV = fWeightsV.Width ();
0700     uint32 widthH = fWeightsH.Width ();
0701 
0702     int32 offsetV = fWeightsV.Offset ();
0703     int32 offsetH = fWeightsH.Offset ();
0704 
0705     uint32 stepH = fWeightsH.Step ();
0706 
0707     const int32 *rowCoords = fRowCoords.Coords (0        );
0708     const int32 *colCoords = fColCoords.Coords (dstArea.l);
0709 
0710     if (fSrcPixelType == ttFloat)
0711         {
0712 
0713         const real32 *weightsH = fWeightsH.Weights32 (0);
0714 
0715         real32 *tPtr = fTempBuffer [threadIndex]->Buffer_real32 ();
0716 
0717         real32 *ttPtr = tPtr + offsetH - srcArea.l;
0718 
0719         for (int32 dstRow = dstArea.t; dstRow < dstArea.b; dstRow++)
0720             {
0721 
0722             int32 rowCoord = rowCoords [dstRow];
0723 
0724             int32 rowFract = rowCoord & kResampleSubsampleMask;
0725 
0726             const real32 *weightsV = fWeightsV.Weights32 (rowFract);
0727 
0728             int32 srcRow = (rowCoord >> kResampleSubsampleBits) + offsetV;
0729 
0730             for (uint32 plane = 0; plane < dstBuffer.fPlanes; plane++)
0731                 {
0732 
0733                 const real32 *sPtr = srcBuffer.ConstPixel_real32 (srcRow,
0734                                                                   srcArea.l,
0735                                                                   plane);
0736 
0737                 DoResampleDown32 (sPtr,
0738                                   tPtr,
0739                                   srcCols,
0740                                   srcBuffer.fRowStep,
0741                                   weightsV,
0742                                   widthV);
0743 
0744                 real32 *dPtr = dstBuffer.DirtyPixel_real32 (dstRow,
0745                                                             dstArea.l,
0746                                                             plane);
0747 
0748                 DoResampleAcross32 (ttPtr,
0749                                     dPtr,
0750                                     dstCols,
0751                                     colCoords,
0752                                     weightsH,
0753                                     widthH,
0754                                     stepH);
0755 
0756                 }
0757 
0758             }
0759 
0760         }
0761 
0762     else
0763         {
0764 
0765         const int16 *weightsH = fWeightsH.Weights16 (0);
0766 
0767         uint16 *tPtr = fTempBuffer [threadIndex]->Buffer_uint16 ();
0768 
0769         uint16 *ttPtr = tPtr + offsetH - srcArea.l;
0770 
0771         uint32 pixelRange = fDstImage.PixelRange ();
0772 
0773         for (int32 dstRow = dstArea.t; dstRow < dstArea.b; dstRow++)
0774             {
0775 
0776             int32 rowCoord = rowCoords [dstRow];
0777 
0778             int32 rowFract = rowCoord & kResampleSubsampleMask;
0779 
0780             const int16 *weightsV = fWeightsV.Weights16 (rowFract);
0781 
0782             int32 srcRow = (rowCoord >> kResampleSubsampleBits) + offsetV;
0783 
0784             for (uint32 plane = 0; plane < dstBuffer.fPlanes; plane++)
0785                 {
0786 
0787                 const uint16 *sPtr = srcBuffer.ConstPixel_uint16 (srcRow,
0788                                                                   srcArea.l,
0789                                                                   plane);
0790 
0791                 DoResampleDown16 (sPtr,
0792                                   tPtr,
0793                                   srcCols,
0794                                   srcBuffer.fRowStep,
0795                                   weightsV,
0796                                   widthV,
0797                                   pixelRange);
0798 
0799                 uint16 *dPtr = dstBuffer.DirtyPixel_uint16 (dstRow,
0800                                                             dstArea.l,
0801                                                             plane);
0802 
0803                 DoResampleAcross16 (ttPtr,
0804                                     dPtr,
0805                                     dstCols,
0806                                     colCoords,
0807                                     weightsH,
0808                                     widthH,
0809                                     stepH,
0810                                     pixelRange);
0811 
0812                 }
0813 
0814             }
0815 
0816         }
0817 
0818     }
0819 
0820 /*****************************************************************************/
0821 
0822 void ResampleImage (dng_host &host,
0823                     const dng_image &srcImage,
0824                     dng_image &dstImage,
0825                     const dng_rect &srcBounds,
0826                     const dng_rect &dstBounds,
0827                     const dng_resample_function &kernel)
0828     {
0829 
0830     dng_resample_task task (srcImage,
0831                             dstImage,
0832                             srcBounds,
0833                             dstBounds,
0834                             kernel);
0835 
0836     host.PerformAreaTask (task,
0837                           dstBounds);
0838 
0839     }
0840 
0841 /*****************************************************************************/