Coverage Report

Created: 2025-11-16 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/MapServer/src/mapresample.c
Line
Count
Source
1
/******************************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  Assorted code related to resampling rasters.
6
 * Author:   Frank Warmerdam, warmerdam@pobox.com
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 1996-2005 Regents of the University of Minnesota.
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a
12
 * copy of this software and associated documentation files (the "Software"),
13
 * to deal in the Software without restriction, including without limitation
14
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15
 * and/or sell copies of the Software, and to permit persons to whom the
16
 * Software is furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies of this Software or works derived from this Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27
 * DEALINGS IN THE SOFTWARE.
28
 *****************************************************************************/
29
30
#include <assert.h>
31
#include "mapresample.h"
32
#include "mapthread.h"
33
34
#define SKIP_MASK(x, y)                                                        \
35
0
  (mask_rb && !*(mask_rb->data.rgba.a + (y)*mask_rb->data.rgba.row_step +      \
36
0
                 (x)*mask_rb->data.rgba.pixel_step))
37
38
/************************************************************************/
39
/*                          InvGeoTransform()                           */
40
/*                                                                      */
41
/*      Invert a standard 3x2 "GeoTransform" style matrix with an       */
42
/*      implicit [1 0 0] final row.                                     */
43
/************************************************************************/
44
45
int InvGeoTransform(double *gt_in, double *gt_out)
46
47
430
{
48
430
  double det, inv_det;
49
50
  /* we assume a 3rd row that is [1 0 0] */
51
52
  /* Compute determinate */
53
54
430
  det = gt_in[1] * gt_in[5] - gt_in[2] * gt_in[4];
55
56
430
  if (fabs(det) < 0.000000000000001)
57
0
    return 0;
58
59
430
  inv_det = 1.0 / det;
60
61
  /* compute adjoint, and divide by determinate */
62
63
430
  gt_out[1] = gt_in[5] * inv_det;
64
430
  gt_out[4] = -gt_in[4] * inv_det;
65
66
430
  gt_out[2] = -gt_in[2] * inv_det;
67
430
  gt_out[5] = gt_in[1] * inv_det;
68
69
430
  gt_out[0] = (gt_in[2] * gt_in[3] - gt_in[0] * gt_in[5]) * inv_det;
70
430
  gt_out[3] = (-gt_in[1] * gt_in[3] + gt_in[0] * gt_in[4]) * inv_det;
71
72
430
  return 1;
73
430
}
74
75
/************************************************************************/
76
/*                      msNearestRasterResample()                       */
77
/************************************************************************/
78
79
static int msNearestRasterResampler(
80
    imageObj *psSrcImage, rasterBufferObj *src_rb, imageObj *psDstImage,
81
    rasterBufferObj *dst_rb, SimpleTransformer pfnTransform, void *pCBData,
82
    int debug, rasterBufferObj *mask_rb, int bWrapAtLeftRight)
83
84
0
{
85
0
  double *x, *y;
86
0
  int nDstX, nDstY;
87
0
  int *panSuccess;
88
0
  int nDstXSize = psDstImage->width;
89
0
  int nDstYSize = psDstImage->height;
90
0
  int nSrcXSize = psSrcImage->width;
91
0
  int nSrcYSize = psSrcImage->height;
92
0
  int nFailedPoints = 0, nSetPoints = 0;
93
0
  assert(!MS_RENDERER_PLUGIN(psSrcImage->format) ||
94
0
         src_rb->type == MS_BUFFER_BYTE_RGBA);
95
96
0
  x = (double *)msSmallMalloc(sizeof(double) * nDstXSize);
97
0
  y = (double *)msSmallMalloc(sizeof(double) * nDstXSize);
98
0
  panSuccess = (int *)msSmallMalloc(sizeof(int) * nDstXSize);
99
100
0
  for (nDstY = 0; nDstY < nDstYSize; nDstY++) {
101
0
    for (nDstX = 0; nDstX < nDstXSize; nDstX++) {
102
0
      x[nDstX] = nDstX + 0.5;
103
0
      y[nDstX] = nDstY + 0.5;
104
0
    }
105
106
0
    pfnTransform(pCBData, nDstXSize, x, y, panSuccess);
107
108
0
    for (nDstX = 0; nDstX < nDstXSize; nDstX++) {
109
0
      int nSrcX, nSrcY;
110
0
      if (SKIP_MASK(nDstX, nDstY))
111
0
        continue;
112
113
0
      if (!panSuccess[nDstX]) {
114
0
        nFailedPoints++;
115
0
        continue;
116
0
      }
117
118
0
      nSrcX = (int)x[nDstX];
119
0
      nSrcY = (int)y[nDstX];
120
121
0
      if (bWrapAtLeftRight && nSrcX >= nSrcXSize && nSrcX < 2 * nSrcXSize)
122
0
        nSrcX -= nSrcXSize;
123
124
      /*
125
       * We test the original floating point values to
126
       * avoid errors related to asymmetric rounding around zero.
127
       * (Also note bug #3120 regarding nearly redundant x/y < 0 checks).
128
       */
129
0
      if (x[nDstX] < 0.0 || y[nDstX] < 0.0 || nSrcX < 0 || nSrcY < 0 ||
130
0
          nSrcX >= nSrcXSize || nSrcY >= nSrcYSize) {
131
0
        continue;
132
0
      }
133
134
0
      if (MS_RENDERER_PLUGIN(psSrcImage->format)) {
135
0
        int src_rb_off;
136
0
        rgbaArrayObj *src, *dst;
137
0
        assert(src_rb && dst_rb);
138
0
        assert(src_rb->type == MS_BUFFER_BYTE_RGBA);
139
0
        src = &src_rb->data.rgba;
140
0
        dst = &dst_rb->data.rgba;
141
0
        src_rb_off = nSrcX * src->pixel_step + nSrcY * src->row_step;
142
143
0
        if (src->a == NULL || src->a[src_rb_off] == 255) {
144
0
          int dst_rb_off;
145
146
0
          dst_rb_off = nDstX * dst->pixel_step + nDstY * dst->row_step;
147
148
0
          nSetPoints++;
149
150
0
          dst->r[dst_rb_off] = src->r[src_rb_off];
151
0
          dst->g[dst_rb_off] = src->g[src_rb_off];
152
0
          dst->b[dst_rb_off] = src->b[src_rb_off];
153
0
          if (dst->a)
154
0
            dst->a[dst_rb_off] = 255;
155
0
        } else if (src->a[src_rb_off] != 0) {
156
0
          int dst_rb_off;
157
158
0
          dst_rb_off = nDstX * dst->pixel_step + nDstY * dst->row_step;
159
160
0
          nSetPoints++;
161
162
          /* actual alpha blending is required */
163
0
          msAlphaBlendPM(
164
0
              src->r[src_rb_off], src->g[src_rb_off], src->b[src_rb_off],
165
0
              src->a[src_rb_off], dst->r + dst_rb_off, dst->g + dst_rb_off,
166
0
              dst->b + dst_rb_off, dst->a ? dst->a + dst_rb_off : NULL);
167
0
        }
168
0
      } else if (MS_RENDERER_RAWDATA(psSrcImage->format)) {
169
0
        int band, src_off, dst_off;
170
171
0
        src_off = nSrcX + nSrcY * psSrcImage->width;
172
173
0
        if (!MS_GET_BIT(psSrcImage->img_mask, src_off))
174
0
          continue;
175
176
0
        nSetPoints++;
177
178
0
        dst_off = nDstX + nDstY * psDstImage->width;
179
180
0
        MS_SET_BIT(psDstImage->img_mask, dst_off);
181
182
0
        for (band = 0; band < psSrcImage->format->bands; band++) {
183
0
          if (psSrcImage->format->imagemode == MS_IMAGEMODE_INT16) {
184
0
            psDstImage->img.raw_16bit[dst_off] =
185
0
                psSrcImage->img.raw_16bit[src_off];
186
0
          } else if (psSrcImage->format->imagemode == MS_IMAGEMODE_FLOAT32) {
187
0
            psDstImage->img.raw_float[dst_off] =
188
0
                psSrcImage->img.raw_float[src_off];
189
0
          } else if (psSrcImage->format->imagemode == MS_IMAGEMODE_BYTE) {
190
0
            psDstImage->img.raw_byte[dst_off] =
191
0
                psSrcImage->img.raw_byte[src_off];
192
0
          } else {
193
0
            assert(0);
194
0
          }
195
0
          src_off += psSrcImage->width * psSrcImage->height;
196
0
          dst_off += psDstImage->width * psDstImage->height;
197
0
        }
198
0
      }
199
0
    }
200
0
  }
201
202
0
  free(panSuccess);
203
0
  free(x);
204
0
  free(y);
205
206
  /* -------------------------------------------------------------------- */
207
  /*      Some debugging output.                                          */
208
  /* -------------------------------------------------------------------- */
209
0
  if (nFailedPoints > 0 && debug) {
210
0
    msDebug("msNearestRasterResampler: "
211
0
            "%d failed to transform, %d actually set.\n",
212
0
            nFailedPoints, nSetPoints);
213
0
  }
214
215
0
  return 0;
216
0
}
217
218
/************************************************************************/
219
/*                            msSourceSample()                          */
220
/************************************************************************/
221
222
static void msSourceSample(imageObj *psSrcImage, rasterBufferObj *rb, int iSrcX,
223
                           int iSrcY, double *padfPixelSum, double dfWeight,
224
                           double *pdfWeightSum)
225
226
0
{
227
0
  if (MS_RENDERER_PLUGIN(psSrcImage->format)) {
228
0
    rgbaArrayObj *rgba;
229
0
    int rb_off;
230
0
    assert(rb && rb->type == MS_BUFFER_BYTE_RGBA);
231
0
    rgba = &(rb->data.rgba);
232
0
    rb_off = iSrcX * rgba->pixel_step + iSrcY * rgba->row_step;
233
234
0
    if (rgba->a == NULL || rgba->a[rb_off] > 1) {
235
0
      padfPixelSum[0] += rgba->r[rb_off] * dfWeight;
236
0
      padfPixelSum[1] += rgba->g[rb_off] * dfWeight;
237
0
      padfPixelSum[2] += rgba->b[rb_off] * dfWeight;
238
239
0
      if (rgba->a == NULL)
240
0
        *pdfWeightSum += dfWeight;
241
0
      else
242
0
        *pdfWeightSum += dfWeight * (rgba->a[rb_off] / 255.0);
243
0
    }
244
0
  } else if (MS_RENDERER_RAWDATA(psSrcImage->format)) {
245
0
    int band;
246
0
    int src_off;
247
248
0
    src_off = iSrcX + iSrcY * psSrcImage->width;
249
250
0
    if (!MS_GET_BIT(psSrcImage->img_mask, src_off))
251
0
      return;
252
253
0
    for (band = 0; band < psSrcImage->format->bands; band++) {
254
0
      if (psSrcImage->format->imagemode == MS_IMAGEMODE_INT16) {
255
0
        int nValue;
256
257
0
        nValue = psSrcImage->img.raw_16bit[src_off];
258
259
0
        padfPixelSum[band] += dfWeight * nValue;
260
0
      } else if (psSrcImage->format->imagemode == MS_IMAGEMODE_FLOAT32) {
261
0
        float fValue;
262
263
0
        fValue = psSrcImage->img.raw_float[src_off];
264
265
0
        padfPixelSum[band] += fValue * dfWeight;
266
0
      } else if (psSrcImage->format->imagemode == MS_IMAGEMODE_FLOAT64) {
267
0
        double dValue;
268
269
0
        dValue = psSrcImage->img.raw_double[src_off];
270
271
0
        padfPixelSum[band] += dValue * dfWeight;
272
0
      } else if (psSrcImage->format->imagemode == MS_IMAGEMODE_BYTE) {
273
0
        int nValue;
274
275
0
        nValue = psSrcImage->img.raw_byte[src_off];
276
277
0
        padfPixelSum[band] += nValue * dfWeight;
278
0
      } else {
279
0
        assert(0);
280
0
        return;
281
0
      }
282
283
0
      src_off += psSrcImage->width * psSrcImage->height;
284
0
    }
285
0
    *pdfWeightSum += dfWeight;
286
0
  }
287
0
}
288
289
/************************************************************************/
290
/*                      msBilinearRasterResample()                      */
291
/************************************************************************/
292
293
static int msBilinearRasterResampler(
294
    imageObj *psSrcImage, rasterBufferObj *src_rb, imageObj *psDstImage,
295
    rasterBufferObj *dst_rb, SimpleTransformer pfnTransform, void *pCBData,
296
    int debug, rasterBufferObj *mask_rb, int bWrapAtLeftRight)
297
298
0
{
299
0
  double *x, *y;
300
0
  int nDstX, nDstY, i;
301
0
  int *panSuccess;
302
0
  int nDstXSize = psDstImage->width;
303
0
  int nDstYSize = psDstImage->height;
304
0
  int nSrcXSize = psSrcImage->width;
305
0
  int nSrcYSize = psSrcImage->height;
306
0
  int nFailedPoints = 0, nSetPoints = 0;
307
0
  double *padfPixelSum;
308
0
  int bandCount = MS_MAX(4, psSrcImage->format->bands);
309
310
0
  padfPixelSum = (double *)msSmallMalloc(sizeof(double) * bandCount);
311
312
0
  x = (double *)msSmallMalloc(sizeof(double) * nDstXSize);
313
0
  y = (double *)msSmallMalloc(sizeof(double) * nDstXSize);
314
0
  panSuccess = (int *)msSmallMalloc(sizeof(int) * nDstXSize);
315
316
0
  for (nDstY = 0; nDstY < nDstYSize; nDstY++) {
317
0
    for (nDstX = 0; nDstX < nDstXSize; nDstX++) {
318
0
      x[nDstX] = nDstX + 0.5;
319
0
      y[nDstX] = nDstY + 0.5;
320
0
    }
321
322
0
    pfnTransform(pCBData, nDstXSize, x, y, panSuccess);
323
324
0
    for (nDstX = 0; nDstX < nDstXSize; nDstX++) {
325
0
      int nSrcX, nSrcY, nSrcX2, nSrcY2;
326
0
      double dfRatioX2, dfRatioY2, dfWeightSum = 0.0;
327
0
      if (SKIP_MASK(nDstX, nDstY))
328
0
        continue;
329
330
0
      if (!panSuccess[nDstX]) {
331
0
        nFailedPoints++;
332
0
        continue;
333
0
      }
334
335
      /* If we are right off the source, skip this pixel */
336
0
      nSrcX = (int)floor(x[nDstX]);
337
0
      nSrcY = (int)floor(y[nDstX]);
338
0
      if (nSrcX < 0 || (!bWrapAtLeftRight && nSrcX >= nSrcXSize) || nSrcY < 0 ||
339
0
          nSrcY >= nSrcYSize)
340
0
        continue;
341
342
      /*
343
      ** Offset to treat TL pixel corners as pixel location instead
344
      ** of the center.
345
      */
346
0
      x[nDstX] -= 0.5;
347
0
      y[nDstX] -= 0.5;
348
349
0
      nSrcX = (int)floor(x[nDstX]);
350
0
      nSrcY = (int)floor(y[nDstX]);
351
352
0
      nSrcX2 = nSrcX + 1;
353
0
      nSrcY2 = nSrcY + 1;
354
355
0
      dfRatioX2 = x[nDstX] - nSrcX;
356
0
      dfRatioY2 = y[nDstX] - nSrcY;
357
358
      /* Trim in stuff one pixel off the edge */
359
0
      nSrcX = MS_MAX(nSrcX, 0);
360
0
      nSrcY = MS_MAX(nSrcY, 0);
361
0
      if (!bWrapAtLeftRight)
362
0
        nSrcX2 = MS_MIN(nSrcX2, nSrcXSize - 1);
363
0
      nSrcY2 = MS_MIN(nSrcY2, nSrcYSize - 1);
364
365
0
      memset(padfPixelSum, 0, sizeof(double) * bandCount);
366
367
0
      msSourceSample(psSrcImage, src_rb, nSrcX % nSrcXSize, nSrcY, padfPixelSum,
368
0
                     (1.0 - dfRatioX2) * (1.0 - dfRatioY2), &dfWeightSum);
369
370
0
      msSourceSample(psSrcImage, src_rb, nSrcX2 % nSrcXSize, nSrcY,
371
0
                     padfPixelSum, (dfRatioX2) * (1.0 - dfRatioY2),
372
0
                     &dfWeightSum);
373
374
0
      msSourceSample(psSrcImage, src_rb, nSrcX % nSrcXSize, nSrcY2,
375
0
                     padfPixelSum, (1.0 - dfRatioX2) * (dfRatioY2),
376
0
                     &dfWeightSum);
377
378
0
      msSourceSample(psSrcImage, src_rb, nSrcX2 % nSrcXSize, nSrcY2,
379
0
                     padfPixelSum, (dfRatioX2) * (dfRatioY2), &dfWeightSum);
380
381
0
      if (dfWeightSum == 0.0)
382
0
        continue;
383
384
0
      if (MS_RENDERER_PLUGIN(psSrcImage->format)) {
385
0
        assert(src_rb && dst_rb);
386
0
        assert(src_rb->type == MS_BUFFER_BYTE_RGBA);
387
0
        assert(src_rb->type == dst_rb->type);
388
389
0
        nSetPoints++;
390
391
0
        if (dfWeightSum > 0.001) {
392
0
          int dst_rb_off = nDstX * dst_rb->data.rgba.pixel_step +
393
0
                           nDstY * dst_rb->data.rgba.row_step;
394
0
          unsigned char red, green, blue, alpha;
395
396
0
          red = (unsigned char)MS_MAX(0, MS_MIN(255, padfPixelSum[0]));
397
0
          green = (unsigned char)MS_MAX(0, MS_MIN(255, padfPixelSum[1]));
398
0
          blue = (unsigned char)MS_MAX(0, MS_MIN(255, padfPixelSum[2]));
399
0
          alpha = (unsigned char)MS_MAX(0, MS_MIN(255, 255.5 * dfWeightSum));
400
401
0
          msAlphaBlendPM(
402
0
              red, green, blue, alpha, dst_rb->data.rgba.r + dst_rb_off,
403
0
              dst_rb->data.rgba.g + dst_rb_off,
404
0
              dst_rb->data.rgba.b + dst_rb_off,
405
0
              (dst_rb->data.rgba.a == NULL) ? NULL
406
0
                                            : dst_rb->data.rgba.a + dst_rb_off);
407
0
        }
408
0
      } else if (MS_RENDERER_RAWDATA(psSrcImage->format)) {
409
0
        int band;
410
0
        int dst_off = nDstX + nDstY * psDstImage->width;
411
412
0
        for (i = 0; i < bandCount; i++)
413
0
          padfPixelSum[i] /= dfWeightSum;
414
415
0
        MS_SET_BIT(psDstImage->img_mask, dst_off);
416
417
0
        for (band = 0; band < psSrcImage->format->bands; band++) {
418
0
          if (psSrcImage->format->imagemode == MS_IMAGEMODE_INT16) {
419
0
            psDstImage->img.raw_16bit[dst_off] = (short)padfPixelSum[band];
420
0
          } else if (psSrcImage->format->imagemode == MS_IMAGEMODE_FLOAT32) {
421
0
            psDstImage->img.raw_float[dst_off] = (float)padfPixelSum[band];
422
0
          } else if (psSrcImage->format->imagemode == MS_IMAGEMODE_BYTE) {
423
0
            psDstImage->img.raw_byte[dst_off] =
424
0
                (unsigned char)MS_MAX(0, MS_MIN(255, padfPixelSum[band]));
425
0
          }
426
427
0
          dst_off += psDstImage->width * psDstImage->height;
428
0
        }
429
0
      }
430
0
    }
431
0
  }
432
433
0
  free(padfPixelSum);
434
0
  free(panSuccess);
435
0
  free(x);
436
0
  free(y);
437
438
  /* -------------------------------------------------------------------- */
439
  /*      Some debugging output.                                          */
440
  /* -------------------------------------------------------------------- */
441
0
  if (nFailedPoints > 0 && debug) {
442
0
    msDebug("msBilinearRasterResampler: "
443
0
            "%d failed to transform, %d actually set.\n",
444
0
            nFailedPoints, nSetPoints);
445
0
  }
446
447
0
  return 0;
448
0
}
449
450
/************************************************************************/
451
/*                          msAverageSample()                           */
452
/************************************************************************/
453
454
static int msAverageSample(imageObj *psSrcImage, rasterBufferObj *src_rb,
455
                           double dfXMin, double dfYMin, double dfXMax,
456
                           double dfYMax, double *padfPixelSum,
457
                           double *pdfAlpha01)
458
459
0
{
460
0
  int nXMin, nXMax, nYMin, nYMax, iX, iY;
461
0
  double dfWeightSum = 0.0;
462
0
  double dfMaxWeight = 0.0;
463
464
0
  nXMin = (int)dfXMin;
465
0
  nYMin = (int)dfYMin;
466
0
  nXMax = (int)ceil(dfXMax);
467
0
  nYMax = (int)ceil(dfYMax);
468
469
0
  *pdfAlpha01 = 0.0;
470
471
0
  for (iY = nYMin; iY < nYMax; iY++) {
472
0
    double dfYCellMin, dfYCellMax;
473
474
0
    dfYCellMin = MS_MAX(iY, dfYMin);
475
0
    dfYCellMax = MS_MIN(iY + 1, dfYMax);
476
477
0
    for (iX = nXMin; iX < nXMax; iX++) {
478
0
      double dfXCellMin, dfXCellMax, dfWeight;
479
480
0
      dfXCellMin = MS_MAX(iX, dfXMin);
481
0
      dfXCellMax = MS_MIN(iX + 1, dfXMax);
482
483
0
      dfWeight = (dfXCellMax - dfXCellMin) * (dfYCellMax - dfYCellMin);
484
485
0
      msSourceSample(psSrcImage, src_rb, iX, iY, padfPixelSum, dfWeight,
486
0
                     &dfWeightSum);
487
0
      dfMaxWeight += dfWeight;
488
0
    }
489
0
  }
490
491
0
  if (dfWeightSum == 0.0)
492
0
    return MS_FALSE;
493
494
0
  for (iX = 0; iX < 4; iX++)
495
0
    padfPixelSum[iX] /= dfWeightSum;
496
497
0
  *pdfAlpha01 = dfWeightSum / dfMaxWeight;
498
499
0
  return MS_TRUE;
500
0
}
501
502
/************************************************************************/
503
/*                      msAverageRasterResample()                       */
504
/************************************************************************/
505
506
static int
507
msAverageRasterResampler(imageObj *psSrcImage, rasterBufferObj *src_rb,
508
                         imageObj *psDstImage, rasterBufferObj *dst_rb,
509
                         SimpleTransformer pfnTransform, void *pCBData,
510
                         int debug, rasterBufferObj *mask_rb)
511
512
0
{
513
0
  double *x1, *y1, *x2, *y2;
514
0
  int nDstX, nDstY;
515
0
  int *panSuccess1, *panSuccess2;
516
0
  int nDstXSize = psDstImage->width;
517
0
  int nDstYSize = psDstImage->height;
518
0
  int nFailedPoints = 0, nSetPoints = 0;
519
0
  double *padfPixelSum;
520
521
0
  int bandCount = MS_MAX(4, psSrcImage->format->bands);
522
523
0
  padfPixelSum = (double *)msSmallMalloc(sizeof(double) * bandCount);
524
525
0
  x1 = (double *)msSmallMalloc(sizeof(double) * (nDstXSize + 1));
526
0
  y1 = (double *)msSmallMalloc(sizeof(double) * (nDstXSize + 1));
527
0
  x2 = (double *)msSmallMalloc(sizeof(double) * (nDstXSize + 1));
528
0
  y2 = (double *)msSmallMalloc(sizeof(double) * (nDstXSize + 1));
529
0
  panSuccess1 = (int *)msSmallMalloc(sizeof(int) * (nDstXSize + 1));
530
0
  panSuccess2 = (int *)msSmallMalloc(sizeof(int) * (nDstXSize + 1));
531
532
0
  for (nDstY = 0; nDstY < nDstYSize; nDstY++) {
533
0
    for (nDstX = 0; nDstX <= nDstXSize; nDstX++) {
534
0
      x1[nDstX] = nDstX;
535
0
      y1[nDstX] = nDstY;
536
0
      x2[nDstX] = nDstX;
537
0
      y2[nDstX] = nDstY + 1;
538
0
    }
539
540
0
    pfnTransform(pCBData, nDstXSize + 1, x1, y1, panSuccess1);
541
0
    pfnTransform(pCBData, nDstXSize + 1, x2, y2, panSuccess2);
542
543
0
    for (nDstX = 0; nDstX < nDstXSize; nDstX++) {
544
0
      double dfXMin, dfYMin, dfXMax, dfYMax;
545
0
      double dfAlpha01;
546
0
      if (SKIP_MASK(nDstX, nDstY))
547
0
        continue;
548
549
      /* Do not generate a pixel unless all four corners transformed */
550
0
      if (!panSuccess1[nDstX] || !panSuccess1[nDstX + 1] ||
551
0
          !panSuccess2[nDstX] || !panSuccess2[nDstX + 1]) {
552
0
        nFailedPoints++;
553
0
        continue;
554
0
      }
555
556
0
      dfXMin = MS_MIN(MS_MIN(x1[nDstX], x1[nDstX + 1]),
557
0
                      MS_MIN(x2[nDstX], x2[nDstX + 1]));
558
0
      dfYMin = MS_MIN(MS_MIN(y1[nDstX], y1[nDstX + 1]),
559
0
                      MS_MIN(y2[nDstX], y2[nDstX + 1]));
560
0
      dfXMax = MS_MAX(MS_MAX(x1[nDstX], x1[nDstX + 1]),
561
0
                      MS_MAX(x2[nDstX], x2[nDstX + 1]));
562
0
      dfYMax = MS_MAX(MS_MAX(y1[nDstX], y1[nDstX + 1]),
563
0
                      MS_MAX(y2[nDstX], y2[nDstX + 1]));
564
565
0
      dfXMin = MS_MIN(MS_MAX(dfXMin, 0), psSrcImage->width + 1);
566
0
      dfYMin = MS_MIN(MS_MAX(dfYMin, 0), psSrcImage->height + 1);
567
0
      dfXMax = MS_MIN(MS_MAX(-1, dfXMax), psSrcImage->width);
568
0
      dfYMax = MS_MIN(MS_MAX(-1, dfYMax), psSrcImage->height);
569
570
0
      memset(padfPixelSum, 0, sizeof(double) * bandCount);
571
572
0
      if (!msAverageSample(psSrcImage, src_rb, dfXMin, dfYMin, dfXMax, dfYMax,
573
0
                           padfPixelSum, &dfAlpha01))
574
0
        continue;
575
576
0
      if (MS_RENDERER_PLUGIN(psSrcImage->format)) {
577
0
        assert(dst_rb && src_rb);
578
0
        assert(dst_rb->type == MS_BUFFER_BYTE_RGBA);
579
0
        assert(src_rb->type == dst_rb->type);
580
581
0
        nSetPoints++;
582
583
0
        if (dfAlpha01 > 0) {
584
0
          unsigned char red, green, blue, alpha;
585
586
0
          red = (unsigned char)MS_MAX(0, MS_MIN(255, padfPixelSum[0] + 0.5));
587
0
          green = (unsigned char)MS_MAX(0, MS_MIN(255, padfPixelSum[1] + 0.5));
588
0
          blue = (unsigned char)MS_MAX(0, MS_MIN(255, padfPixelSum[2] + 0.5));
589
0
          alpha = (unsigned char)MS_MAX(0, MS_MIN(255, 255 * dfAlpha01 + 0.5));
590
591
0
          RB_MIX_PIXEL(dst_rb, nDstX, nDstY, red, green, blue, alpha);
592
0
        }
593
0
      } else if (MS_RENDERER_RAWDATA(psSrcImage->format)) {
594
0
        int band;
595
0
        int dst_off = nDstX + nDstY * psDstImage->width;
596
597
0
        MS_SET_BIT(psDstImage->img_mask, dst_off);
598
599
0
        for (band = 0; band < psSrcImage->format->bands; band++) {
600
0
          if (psSrcImage->format->imagemode == MS_IMAGEMODE_INT16) {
601
0
            psDstImage->img.raw_16bit[dst_off] =
602
0
                (short)(padfPixelSum[band] + 0.5);
603
0
          } else if (psSrcImage->format->imagemode == MS_IMAGEMODE_FLOAT32) {
604
0
            psDstImage->img.raw_float[dst_off] = (float)padfPixelSum[band];
605
0
          } else if (psSrcImage->format->imagemode == MS_IMAGEMODE_FLOAT64) {
606
0
            psDstImage->img.raw_double[dst_off] = (double)padfPixelSum[band];
607
0
          } else if (psSrcImage->format->imagemode == MS_IMAGEMODE_BYTE) {
608
0
            psDstImage->img.raw_byte[dst_off] =
609
0
                (unsigned char)padfPixelSum[band];
610
0
          }
611
612
0
          dst_off += psDstImage->width * psDstImage->height;
613
0
        }
614
0
      }
615
0
    }
616
0
  }
617
618
0
  free(padfPixelSum);
619
0
  free(panSuccess1);
620
0
  free(x1);
621
0
  free(y1);
622
0
  free(panSuccess2);
623
0
  free(x2);
624
0
  free(y2);
625
626
  /* -------------------------------------------------------------------- */
627
  /*      Some debugging output.                                          */
628
  /* -------------------------------------------------------------------- */
629
0
  if (nFailedPoints > 0 && debug) {
630
0
    msDebug("msAverageRasterResampler: "
631
0
            "%d failed to transform, %d actually set.\n",
632
0
            nFailedPoints, nSetPoints);
633
0
  }
634
635
0
  return 0;
636
0
}
637
638
/************************************************************************/
639
/* ==================================================================== */
640
/*      PROJ based transformer.         */
641
/* ==================================================================== */
642
/************************************************************************/
643
644
typedef struct {
645
  projectionObj *psSrcProjObj;
646
  int bSrcIsGeographic;
647
  double adfInvSrcGeoTransform[6];
648
649
  projectionObj *psDstProjObj;
650
  int bDstIsGeographic;
651
  double adfDstGeoTransform[6];
652
653
  int bUseProj;
654
  reprojectionObj *pReprojectionDstToSrc;
655
} msProjTransformInfo;
656
657
/************************************************************************/
658
/*                       msInitProjTransformer()                        */
659
/************************************************************************/
660
661
void *msInitProjTransformer(projectionObj *psSrc, double *padfSrcGeoTransform,
662
                            projectionObj *psDst, double *padfDstGeoTransform)
663
664
0
{
665
0
  int backup_src_need_gt;
666
0
  int backup_dst_need_gt;
667
0
  msProjTransformInfo *psPTInfo;
668
669
0
  psPTInfo =
670
0
      (msProjTransformInfo *)msSmallCalloc(1, sizeof(msProjTransformInfo));
671
672
  /* -------------------------------------------------------------------- */
673
  /*      We won't even use PROJ.4 if either coordinate system is         */
674
  /*      NULL.                                                           */
675
  /* -------------------------------------------------------------------- */
676
0
  backup_src_need_gt = psSrc->gt.need_geotransform;
677
0
  psSrc->gt.need_geotransform = 0;
678
0
  backup_dst_need_gt = psDst->gt.need_geotransform;
679
0
  psDst->gt.need_geotransform = 0;
680
0
  psPTInfo->bUseProj = (psSrc->proj != NULL && psDst->proj != NULL &&
681
0
                        msProjectionsDiffer(psSrc, psDst));
682
0
  psSrc->gt.need_geotransform = backup_src_need_gt;
683
0
  psDst->gt.need_geotransform = backup_dst_need_gt;
684
685
  /* -------------------------------------------------------------------- */
686
  /*      Record source image information.  We invert the source          */
687
  /*      transformation for more convenient inverse application in       */
688
  /*      the transformer.                                                */
689
  /* -------------------------------------------------------------------- */
690
0
  psPTInfo->psSrcProjObj = psSrc;
691
0
  if (psPTInfo->bUseProj)
692
0
    psPTInfo->bSrcIsGeographic = msProjIsGeographicCRS(psSrc);
693
0
  else
694
0
    psPTInfo->bSrcIsGeographic = MS_FALSE;
695
696
0
  if (!InvGeoTransform(padfSrcGeoTransform, psPTInfo->adfInvSrcGeoTransform)) {
697
0
    free(psPTInfo);
698
0
    return NULL;
699
0
  }
700
701
  /* -------------------------------------------------------------------- */
702
  /*      Record destination image information.                           */
703
  /* -------------------------------------------------------------------- */
704
0
  psPTInfo->psDstProjObj = psDst;
705
0
  if (psPTInfo->bUseProj)
706
0
    psPTInfo->bDstIsGeographic = msProjIsGeographicCRS(psDst);
707
0
  else
708
0
    psPTInfo->bDstIsGeographic = MS_FALSE;
709
0
  memcpy(psPTInfo->adfDstGeoTransform, padfDstGeoTransform, sizeof(double) * 6);
710
711
0
  if (psPTInfo->bUseProj) {
712
0
    psPTInfo->pReprojectionDstToSrc = msProjectCreateReprojector(
713
0
        psPTInfo->psDstProjObj, psPTInfo->psSrcProjObj);
714
0
    if (!psPTInfo->pReprojectionDstToSrc) {
715
0
      free(psPTInfo);
716
0
      return NULL;
717
0
    }
718
0
  }
719
720
0
  return psPTInfo;
721
0
}
722
723
/************************************************************************/
724
/*                       msFreeProjTransformer()                        */
725
/************************************************************************/
726
727
void msFreeProjTransformer(void *pCBData)
728
729
0
{
730
0
  if (pCBData) {
731
0
    msProjTransformInfo *psPTInfo = (msProjTransformInfo *)pCBData;
732
0
    msProjectDestroyReprojector(psPTInfo->pReprojectionDstToSrc);
733
0
  }
734
0
  free(pCBData);
735
0
}
736
737
/************************************************************************/
738
/*                          msProjTransformer                           */
739
/************************************************************************/
740
741
int msProjTransformer(void *pCBData, int nPoints, double *x, double *y,
742
                      int *panSuccess)
743
744
0
{
745
0
  int i;
746
0
  msProjTransformInfo *psPTInfo = (msProjTransformInfo *)pCBData;
747
0
  double x_out;
748
749
  /* -------------------------------------------------------------------- */
750
  /*      Transform into destination georeferenced space.                 */
751
  /* -------------------------------------------------------------------- */
752
0
  for (i = 0; i < nPoints; i++) {
753
0
    x_out = psPTInfo->adfDstGeoTransform[0] +
754
0
            psPTInfo->adfDstGeoTransform[1] * x[i] +
755
0
            psPTInfo->adfDstGeoTransform[2] * y[i];
756
0
    y[i] = psPTInfo->adfDstGeoTransform[3] +
757
0
           psPTInfo->adfDstGeoTransform[4] * x[i] +
758
0
           psPTInfo->adfDstGeoTransform[5] * y[i];
759
0
    x[i] = x_out;
760
761
0
    panSuccess[i] = 1;
762
0
  }
763
764
0
  if (psPTInfo->bUseProj) {
765
0
    if (msProjectTransformPoints(psPTInfo->pReprojectionDstToSrc, nPoints, x,
766
0
                                 y) != MS_SUCCESS) {
767
0
      for (i = 0; i < nPoints; i++)
768
0
        panSuccess[i] = 0;
769
770
0
      return MS_FALSE;
771
0
    }
772
0
    for (i = 0; i < nPoints; i++) {
773
0
      if (x[i] == HUGE_VAL || y[i] == HUGE_VAL)
774
0
        panSuccess[i] = 0;
775
0
    }
776
0
  }
777
778
  /* -------------------------------------------------------------------- */
779
  /*      Transform to source raster space.                               */
780
  /* -------------------------------------------------------------------- */
781
0
  for (i = 0; i < nPoints; i++) {
782
0
    if (panSuccess[i]) {
783
0
      x_out = psPTInfo->adfInvSrcGeoTransform[0] +
784
0
              psPTInfo->adfInvSrcGeoTransform[1] * x[i] +
785
0
              psPTInfo->adfInvSrcGeoTransform[2] * y[i];
786
0
      y[i] = psPTInfo->adfInvSrcGeoTransform[3] +
787
0
             psPTInfo->adfInvSrcGeoTransform[4] * x[i] +
788
0
             psPTInfo->adfInvSrcGeoTransform[5] * y[i];
789
0
      x[i] = x_out;
790
0
    } else {
791
0
      x[i] = -1;
792
0
      y[i] = -1;
793
0
    }
794
0
  }
795
796
0
  return 1;
797
0
}
798
799
/************************************************************************/
800
/* ==================================================================== */
801
/*      Approximate transformer.                                        */
802
/* ==================================================================== */
803
/************************************************************************/
804
805
typedef struct {
806
  SimpleTransformer pfnBaseTransformer;
807
  void *pBaseCBData;
808
809
  double dfMaxError;
810
} msApproxTransformInfo;
811
812
/************************************************************************/
813
/*                      msInitApproxTransformer()                       */
814
/************************************************************************/
815
816
static void *msInitApproxTransformer(SimpleTransformer pfnBaseTransformer,
817
                                     void *pBaseCBData, double dfMaxError)
818
819
0
{
820
0
  msApproxTransformInfo *psATInfo;
821
822
0
  psATInfo =
823
0
      (msApproxTransformInfo *)msSmallMalloc(sizeof(msApproxTransformInfo));
824
0
  psATInfo->pfnBaseTransformer = pfnBaseTransformer;
825
0
  psATInfo->pBaseCBData = pBaseCBData;
826
0
  psATInfo->dfMaxError = dfMaxError;
827
828
0
  return psATInfo;
829
0
}
830
831
/************************************************************************/
832
/*                      msFreeApproxTransformer()                       */
833
/************************************************************************/
834
835
static void msFreeApproxTransformer(void *pCBData)
836
837
0
{
838
0
  free(pCBData);
839
0
}
840
841
/************************************************************************/
842
/*                         msApproxTransformer                          */
843
/************************************************************************/
844
845
static int msApproxTransformer(void *pCBData, int nPoints, double *x, double *y,
846
                               int *panSuccess)
847
848
0
{
849
0
  msApproxTransformInfo *psATInfo = (msApproxTransformInfo *)pCBData;
850
0
  double x2[3], y2[3], dfDeltaX, dfDeltaY, dfError, dfDist;
851
0
  int nMiddle, anSuccess2[3], i, bSuccess;
852
853
0
  nMiddle = (nPoints - 1) / 2;
854
855
  /* -------------------------------------------------------------------- */
856
  /*      Bail if our preconditions are not met, or if error is not       */
857
  /*      acceptable.                                                     */
858
  /* -------------------------------------------------------------------- */
859
0
  if (y[0] != y[nPoints - 1] || y[0] != y[nMiddle] || x[0] == x[nPoints - 1] ||
860
0
      x[0] == x[nMiddle] || psATInfo->dfMaxError == 0.0 || nPoints <= 5) {
861
0
    return psATInfo->pfnBaseTransformer(psATInfo->pBaseCBData, nPoints, x, y,
862
0
                                        panSuccess);
863
0
  }
864
865
  /* -------------------------------------------------------------------- */
866
  /*      Transform first, last and middle point.                         */
867
  /* -------------------------------------------------------------------- */
868
0
  x2[0] = x[0];
869
0
  y2[0] = y[0];
870
0
  x2[1] = x[nMiddle];
871
0
  y2[1] = y[nMiddle];
872
0
  x2[2] = x[nPoints - 1];
873
0
  y2[2] = y[nPoints - 1];
874
875
0
  bSuccess = psATInfo->pfnBaseTransformer(psATInfo->pBaseCBData, 3, x2, y2,
876
0
                                          anSuccess2);
877
0
  if (!bSuccess || !anSuccess2[0] || !anSuccess2[1] || !anSuccess2[2])
878
0
    return psATInfo->pfnBaseTransformer(psATInfo->pBaseCBData, nPoints, x, y,
879
0
                                        panSuccess);
880
881
  /* -------------------------------------------------------------------- */
882
  /*      Is the error at the middle acceptable relative to an            */
883
  /*      interpolation of the middle position?                           */
884
  /* -------------------------------------------------------------------- */
885
0
  dfDeltaX = (x2[2] - x2[0]) / (x[nPoints - 1] - x[0]);
886
0
  dfDeltaY = (y2[2] - y2[0]) / (x[nPoints - 1] - x[0]);
887
888
0
  dfError = fabs((x2[0] + dfDeltaX * (x[nMiddle] - x[0])) - x2[1]) +
889
0
            fabs((y2[0] + dfDeltaY * (x[nMiddle] - x[0])) - y2[1]);
890
891
0
  if (dfError > psATInfo->dfMaxError) {
892
0
    bSuccess = msApproxTransformer(psATInfo, nMiddle, x, y, panSuccess);
893
894
0
    if (!bSuccess) {
895
0
      return psATInfo->pfnBaseTransformer(psATInfo->pBaseCBData, nPoints, x, y,
896
0
                                          panSuccess);
897
0
    }
898
899
0
    bSuccess = msApproxTransformer(psATInfo, nPoints - nMiddle, x + nMiddle,
900
0
                                   y + nMiddle, panSuccess + nMiddle);
901
902
0
    if (!bSuccess) {
903
0
      return psATInfo->pfnBaseTransformer(psATInfo->pBaseCBData, nPoints, x, y,
904
0
                                          panSuccess);
905
0
    }
906
907
0
    return 1;
908
0
  }
909
910
  /* -------------------------------------------------------------------- */
911
  /*      Error is OK, linearly interpolate all points along line.        */
912
  /* -------------------------------------------------------------------- */
913
0
  for (i = nPoints - 1; i >= 0; i--) {
914
0
    dfDist = (x[i] - x[0]);
915
0
    y[i] = y2[0] + dfDeltaY * dfDist;
916
0
    x[i] = x2[0] + dfDeltaX * dfDist;
917
0
    panSuccess[i] = 1;
918
0
  }
919
920
0
  return 1;
921
0
}
922
923
/************************************************************************/
924
/*                       msTransformMapToSource()                       */
925
/*                                                                      */
926
/*      Compute the extents of the current map view if transformed      */
927
/*      onto the source raster.                                         */
928
/************************************************************************/
929
930
static int
931
msTransformMapToSource(int nDstXSize, int nDstYSize, double *adfDstGeoTransform,
932
                       projectionObj *psDstProj, int nSrcXSize, int nSrcYSize,
933
                       double *adfInvSrcGeoTransform, projectionObj *psSrcProj,
934
                       rectObj *psSrcExtent, int bUseGrid)
935
936
0
{
937
0
  int nFailures = 0;
938
939
0
#define EDGE_STEPS 10
940
0
#define MAX_SIZE ((EDGE_STEPS + 1) * (EDGE_STEPS + 1))
941
942
0
  int i, nSamples = 0, bOutInit = 0;
943
0
  double dfRatio;
944
0
  double x[MAX_SIZE], y[MAX_SIZE];
945
946
  /* -------------------------------------------------------------------- */
947
  /*      Collect edges in map image pixel/line coordinates               */
948
  /* -------------------------------------------------------------------- */
949
0
  if (!bUseGrid) {
950
0
    for (dfRatio = 0.0; dfRatio <= 1.001; dfRatio += (1.0 / EDGE_STEPS)) {
951
0
      assert(nSamples < MAX_SIZE);
952
0
      x[nSamples] = dfRatio * nDstXSize;
953
0
      y[nSamples++] = 0.0;
954
0
      x[nSamples] = dfRatio * nDstXSize;
955
0
      y[nSamples++] = nDstYSize;
956
0
      x[nSamples] = 0.0;
957
0
      y[nSamples++] = dfRatio * nDstYSize;
958
0
      x[nSamples] = nDstXSize;
959
0
      y[nSamples++] = dfRatio * nDstYSize;
960
0
    }
961
0
  }
962
963
  /* -------------------------------------------------------------------- */
964
  /*      Collect a grid in the hopes of a more accurate region.          */
965
  /* -------------------------------------------------------------------- */
966
0
  else {
967
0
    double dfRatio2;
968
969
0
    for (dfRatio = 0.0; dfRatio <= 1.001; dfRatio += (1.0 / EDGE_STEPS)) {
970
0
      for (dfRatio2 = 0.0; dfRatio2 <= 1.001; dfRatio2 += (1.0 / EDGE_STEPS)) {
971
0
        assert(nSamples < MAX_SIZE);
972
0
        x[nSamples] = dfRatio2 * nDstXSize;
973
0
        y[nSamples++] = dfRatio * nDstYSize;
974
0
      }
975
0
    }
976
0
  }
977
978
  /* -------------------------------------------------------------------- */
979
  /*      transform to map georeferenced units                            */
980
  /* -------------------------------------------------------------------- */
981
0
  for (i = 0; i < nSamples; i++) {
982
0
    double x_out, y_out;
983
984
0
    x_out = adfDstGeoTransform[0] + x[i] * adfDstGeoTransform[1] +
985
0
            y[i] * adfDstGeoTransform[2];
986
987
0
    y_out = adfDstGeoTransform[3] + x[i] * adfDstGeoTransform[4] +
988
0
            y[i] * adfDstGeoTransform[5];
989
990
0
    x[i] = x_out;
991
0
    y[i] = y_out;
992
0
  }
993
994
  /* -------------------------------------------------------------------- */
995
  /*      Transform to layer georeferenced coordinates.                   */
996
  /* -------------------------------------------------------------------- */
997
0
  if (psDstProj->proj && psSrcProj->proj) {
998
0
    reprojectionObj *reprojector =
999
0
        msProjectCreateReprojector(psDstProj, psSrcProj);
1000
0
    if (!reprojector)
1001
0
      return MS_FALSE;
1002
0
    if (msProjectTransformPoints(reprojector, nSamples, x, y) != MS_SUCCESS) {
1003
0
      msProjectDestroyReprojector(reprojector);
1004
0
      return MS_FALSE;
1005
0
    }
1006
0
    msProjectDestroyReprojector(reprojector);
1007
0
  }
1008
1009
  /* -------------------------------------------------------------------- */
1010
  /*      If we just using the edges (not a grid) and we go some          */
1011
  /*      errors, then we need to restart using a grid pattern.           */
1012
  /* -------------------------------------------------------------------- */
1013
0
  if (!bUseGrid) {
1014
0
    for (i = 0; i < nSamples; i++) {
1015
0
      if (x[i] == HUGE_VAL || y[i] == HUGE_VAL) {
1016
0
        return msTransformMapToSource(
1017
0
            nDstXSize, nDstYSize, adfDstGeoTransform, psDstProj, nSrcXSize,
1018
0
            nSrcYSize, adfInvSrcGeoTransform, psSrcProj, psSrcExtent, 1);
1019
0
      }
1020
0
    }
1021
0
  }
1022
1023
  /* -------------------------------------------------------------------- */
1024
  /*      transform to layer raster coordinates, and collect bounds.      */
1025
  /* -------------------------------------------------------------------- */
1026
0
  for (i = 0; i < nSamples; i++) {
1027
0
    double x_out, y_out;
1028
1029
0
    if (x[i] == HUGE_VAL || y[i] == HUGE_VAL) {
1030
0
      nFailures++;
1031
0
      continue;
1032
0
    }
1033
1034
0
    x_out = adfInvSrcGeoTransform[0] + x[i] * adfInvSrcGeoTransform[1] +
1035
0
            y[i] * adfInvSrcGeoTransform[2];
1036
0
    y_out = adfInvSrcGeoTransform[3] + x[i] * adfInvSrcGeoTransform[4] +
1037
0
            y[i] * adfInvSrcGeoTransform[5];
1038
1039
0
    if (!bOutInit) {
1040
0
      psSrcExtent->minx = psSrcExtent->maxx = x_out;
1041
0
      psSrcExtent->miny = psSrcExtent->maxy = y_out;
1042
0
      bOutInit = 1;
1043
0
    } else {
1044
0
      psSrcExtent->minx = MS_MIN(psSrcExtent->minx, x_out);
1045
0
      psSrcExtent->maxx = MS_MAX(psSrcExtent->maxx, x_out);
1046
0
      psSrcExtent->miny = MS_MIN(psSrcExtent->miny, y_out);
1047
0
      psSrcExtent->maxy = MS_MAX(psSrcExtent->maxy, y_out);
1048
0
    }
1049
0
  }
1050
1051
  /* -------------------------------------------------------------------- */
1052
  /*      Deal with discontinuities related to lon_wrap=XXX in source     */
1053
  /*      projection. In that case we must check if the points at         */
1054
  /*      lon_wrap +/- 180deg are in the output raster.                   */
1055
  /* -------------------------------------------------------------------- */
1056
0
  if (bOutInit && msProjIsGeographicCRS(psSrcProj)) {
1057
0
    double dfLonWrap = 0;
1058
0
    int bHasLonWrap = msProjectHasLonWrap(psSrcProj, &dfLonWrap);
1059
1060
0
    if (bHasLonWrap) {
1061
0
      double x2[2], y2[2];
1062
0
      int nCountY = 0;
1063
0
      double dfY = 0.0;
1064
0
      double dfXMinOut = 0.0;
1065
0
      double dfYMinOut = 0.0;
1066
0
      double dfXMaxOut = 0.0;
1067
0
      double dfYMaxOut = 0.0;
1068
0
      const double dfHalfRes = adfDstGeoTransform[1] / 2;
1069
1070
      /* Find out average y coordinate in src projection */
1071
0
      for (i = 0; i < nSamples; i++) {
1072
0
        if (y[i] != HUGE_VAL) {
1073
0
          dfY += y[i];
1074
0
          nCountY++;
1075
0
        }
1076
0
      }
1077
0
      dfY /= nCountY;
1078
1079
      /* Compute bounds of output raster */
1080
0
      for (i = 0; i < 4; i++) {
1081
0
        double dfX =
1082
0
            adfDstGeoTransform[0] +
1083
0
            ((i == 1 || i == 2) ? nDstXSize : 0) * adfDstGeoTransform[1] +
1084
0
            ((i == 1 || i == 3) ? nDstYSize : 0) * adfDstGeoTransform[2];
1085
0
        double dfY =
1086
0
            adfDstGeoTransform[3] +
1087
0
            ((i == 1 || i == 2) ? nDstXSize : 0) * adfDstGeoTransform[4] +
1088
0
            ((i == 1 || i == 3) ? nDstYSize : 0) * adfDstGeoTransform[5];
1089
0
        if (i == 0 || dfX < dfXMinOut)
1090
0
          dfXMinOut = dfX;
1091
0
        if (i == 0 || dfY < dfYMinOut)
1092
0
          dfYMinOut = dfY;
1093
0
        if (i == 0 || dfX > dfXMaxOut)
1094
0
          dfXMaxOut = dfX;
1095
0
        if (i == 0 || dfY > dfYMaxOut)
1096
0
          dfYMaxOut = dfY;
1097
0
      }
1098
1099
0
      x2[0] = dfLonWrap - 180 + 1e-7;
1100
0
      y2[0] = dfY;
1101
1102
0
      x2[1] = dfLonWrap + 180 - 1e-7;
1103
0
      y2[1] = dfY;
1104
1105
0
      {
1106
0
        reprojectionObj *reprojector =
1107
0
            msProjectCreateReprojector(psSrcProj, psDstProj);
1108
0
        if (reprojector) {
1109
0
          msProjectTransformPoints(reprojector, 2, x2, y2);
1110
0
          msProjectDestroyReprojector(reprojector);
1111
0
        }
1112
0
      }
1113
1114
0
      if (x2[0] >= dfXMinOut - dfHalfRes && x2[0] <= dfXMaxOut + dfHalfRes &&
1115
0
          y2[0] >= dfYMinOut && y2[0] <= dfYMaxOut) {
1116
0
        double x_out = adfInvSrcGeoTransform[0] +
1117
0
                       (dfLonWrap - 180) * adfInvSrcGeoTransform[1] +
1118
0
                       dfY * adfInvSrcGeoTransform[2];
1119
0
        double y_out = adfInvSrcGeoTransform[3] +
1120
0
                       (dfLonWrap - 180) * adfInvSrcGeoTransform[4] +
1121
0
                       dfY * adfInvSrcGeoTransform[5];
1122
1123
        /* Does the raster cover, at least, a whole 360 deg range ? */
1124
0
        if (nSrcXSize >= (int)(adfInvSrcGeoTransform[1] * 360)) {
1125
0
          psSrcExtent->minx = 0;
1126
0
          psSrcExtent->maxx = nSrcXSize;
1127
0
        } else {
1128
0
          psSrcExtent->minx = MS_MIN(psSrcExtent->minx, x_out);
1129
0
          psSrcExtent->maxx = MS_MAX(psSrcExtent->maxx, x_out);
1130
0
        }
1131
0
        psSrcExtent->miny = MS_MIN(psSrcExtent->miny, y_out);
1132
0
        psSrcExtent->maxy = MS_MAX(psSrcExtent->maxy, y_out);
1133
0
      }
1134
1135
0
      if (x2[1] >= dfXMinOut - dfHalfRes && x2[1] <= dfXMaxOut + dfHalfRes &&
1136
0
          y2[1] >= dfYMinOut && y2[1] <= dfYMaxOut) {
1137
0
        double x_out = adfInvSrcGeoTransform[0] +
1138
0
                       (dfLonWrap + 180) * adfInvSrcGeoTransform[1] +
1139
0
                       dfY * adfInvSrcGeoTransform[2];
1140
0
        double y_out = adfInvSrcGeoTransform[3] +
1141
0
                       (dfLonWrap + 180) * adfInvSrcGeoTransform[4] +
1142
0
                       dfY * adfInvSrcGeoTransform[5];
1143
1144
        /* Does the raster cover, at least, a whole 360 deg range ? */
1145
0
        if (nSrcXSize >= (int)(adfInvSrcGeoTransform[1] * 360)) {
1146
0
          psSrcExtent->minx = 0;
1147
0
          psSrcExtent->maxx = nSrcXSize;
1148
0
        } else {
1149
0
          psSrcExtent->minx = MS_MIN(psSrcExtent->minx, x_out);
1150
0
          psSrcExtent->maxx = MS_MAX(psSrcExtent->maxx, x_out);
1151
0
        }
1152
0
        psSrcExtent->miny = MS_MIN(psSrcExtent->miny, y_out);
1153
0
        psSrcExtent->maxy = MS_MAX(psSrcExtent->maxy, y_out);
1154
0
      }
1155
0
    }
1156
0
  }
1157
1158
0
  if (!bOutInit)
1159
0
    return MS_FALSE;
1160
1161
  /* -------------------------------------------------------------------- */
1162
  /*      If we had some failures, we need to expand the region to        */
1163
  /*      represent our very coarse sampling grid.                        */
1164
  /* -------------------------------------------------------------------- */
1165
0
  if (nFailures > 0) {
1166
0
    int nGrowAmountX =
1167
0
        (int)(psSrcExtent->maxx - psSrcExtent->minx) / EDGE_STEPS + 1;
1168
0
    int nGrowAmountY =
1169
0
        (int)(psSrcExtent->maxy - psSrcExtent->miny) / EDGE_STEPS + 1;
1170
1171
0
    psSrcExtent->minx = MS_MAX(psSrcExtent->minx - nGrowAmountX, 0);
1172
0
    psSrcExtent->miny = MS_MAX(psSrcExtent->miny - nGrowAmountY, 0);
1173
0
    psSrcExtent->maxx = MS_MIN(psSrcExtent->maxx + nGrowAmountX, nSrcXSize);
1174
0
    psSrcExtent->maxy = MS_MIN(psSrcExtent->maxy + nGrowAmountY, nSrcYSize);
1175
0
  }
1176
1177
0
  return MS_TRUE;
1178
0
}
1179
1180
/************************************************************************/
1181
/*                        msResampleGDALToMap()                         */
1182
/************************************************************************/
1183
1184
int msResampleGDALToMap(mapObj *map, layerObj *layer, imageObj *image,
1185
                        rasterBufferObj *rb, GDALDatasetH hDS)
1186
1187
0
{
1188
0
  int nSrcXSize, nSrcYSize, nDstXSize, nDstYSize;
1189
0
  int result, bSuccess;
1190
0
  double adfSrcGeoTransform[6], adfDstGeoTransform[6];
1191
0
  double adfInvSrcGeoTransform[6], dfNominalCellSize;
1192
0
  rectObj sSrcExtent = {0}, sOrigSrcExtent;
1193
0
  mapObj sDummyMap;
1194
0
  imageObj *srcImage;
1195
0
  void *pTCBData;
1196
0
  void *pACBData;
1197
0
  char **papszAlteredProcessing = NULL;
1198
0
  int nLoadImgXSize, nLoadImgYSize;
1199
0
  double dfOversampleRatio;
1200
0
  rasterBufferObj src_rb, *psrc_rb = NULL, *mask_rb = NULL;
1201
0
  int bAddPixelMargin = MS_TRUE;
1202
0
  int bWrapAtLeftRight = MS_FALSE;
1203
1204
0
  const char *resampleMode = CSLFetchNameValue(layer->processing, "RESAMPLE");
1205
1206
0
  if (resampleMode == NULL)
1207
0
    resampleMode = "NEAREST";
1208
1209
0
  if (layer->mask) {
1210
0
    int ret, maskLayerIdx;
1211
0
    layerObj *maskLayer;
1212
0
    maskLayerIdx = msGetLayerIndex(map, layer->mask);
1213
0
    if (maskLayerIdx == -1) {
1214
0
      msSetError(MS_MISCERR, "Invalid mask layer specified",
1215
0
                 "msResampleGDALToMap()");
1216
0
      return -1;
1217
0
    }
1218
0
    maskLayer = GET_LAYER(map, maskLayerIdx);
1219
0
    mask_rb = msSmallCalloc(1, sizeof(rasterBufferObj));
1220
0
    ret = MS_IMAGE_RENDERER(maskLayer->maskimage)
1221
0
              ->getRasterBufferHandle(maskLayer->maskimage, mask_rb);
1222
0
    if (ret != MS_SUCCESS) {
1223
0
      free(mask_rb);
1224
0
      return -1;
1225
0
    }
1226
0
  }
1227
1228
  /* -------------------------------------------------------------------- */
1229
  /*      We will require source and destination to have a valid          */
1230
  /*      projection object.                                              */
1231
  /* -------------------------------------------------------------------- */
1232
0
  if (map->projection.proj == NULL || layer->projection.proj == NULL) {
1233
0
    if (layer->debug)
1234
0
      msDebug("msResampleGDALToMap(): "
1235
0
              "Either map or layer projection is NULL, assuming compatible.\n");
1236
0
  }
1237
1238
  /* -------------------------------------------------------------------- */
1239
  /*      Initialize some information.                                    */
1240
  /* -------------------------------------------------------------------- */
1241
0
  nDstXSize = image->width;
1242
0
  nDstYSize = image->height;
1243
1244
0
  memcpy(adfDstGeoTransform, map->gt.geotransform, sizeof(double) * 6);
1245
1246
0
  msGetGDALGeoTransform(hDS, map, layer, adfSrcGeoTransform);
1247
1248
0
  nSrcXSize = GDALGetRasterXSize(hDS);
1249
0
  nSrcYSize = GDALGetRasterYSize(hDS);
1250
1251
0
  InvGeoTransform(adfSrcGeoTransform, adfInvSrcGeoTransform);
1252
1253
  /* -------------------------------------------------------------------- */
1254
  /*      We need to find the extents in the source layer projection      */
1255
  /*      of the output requested region.  We will accomplish this by     */
1256
  /*      collecting the extents of a region around the edge of the       */
1257
  /*      destination chunk.                                              */
1258
  /* -------------------------------------------------------------------- */
1259
0
  if (CSLFetchBoolean(layer->processing, "LOAD_WHOLE_IMAGE", FALSE))
1260
0
    bSuccess = FALSE;
1261
0
  else {
1262
0
    bSuccess = msTransformMapToSource(nDstXSize, nDstYSize, adfDstGeoTransform,
1263
0
                                      &(map->projection), nSrcXSize, nSrcYSize,
1264
0
                                      adfInvSrcGeoTransform,
1265
0
                                      &(layer->projection), &sSrcExtent, FALSE);
1266
0
    if (bSuccess) {
1267
      /* -------------------------------------------------------------------- */
1268
      /*      Repeat transformation for a rectangle interior to the output    */
1269
      /*      requested region.  If the latter results in a more extreme y    */
1270
      /*      extent, then extend extents in source layer projection to       */
1271
      /*      southern/northing bounds and entire x extent.                   */
1272
      /* -------------------------------------------------------------------- */
1273
0
      memcpy(&sOrigSrcExtent, &sSrcExtent, sizeof(sSrcExtent));
1274
0
      adfDstGeoTransform[0] = adfDstGeoTransform[0] + adfDstGeoTransform[1];
1275
0
      adfDstGeoTransform[3] = adfDstGeoTransform[3] + adfDstGeoTransform[5];
1276
0
      bSuccess = msTransformMapToSource(
1277
0
          nDstXSize - 2, nDstYSize - 2, adfDstGeoTransform, &(map->projection),
1278
0
          nSrcXSize, nSrcYSize, adfInvSrcGeoTransform, &(layer->projection),
1279
0
          &sSrcExtent, FALSE);
1280
      /* Reset this array to its original value! */
1281
0
      memcpy(adfDstGeoTransform, map->gt.geotransform, sizeof(double) * 6);
1282
1283
0
      if (bSuccess) {
1284
0
        if (sSrcExtent.maxy > sOrigSrcExtent.maxy ||
1285
0
            sSrcExtent.miny < sOrigSrcExtent.miny) {
1286
0
          msDebug("msTransformMapToSource(): extending bounds.\n");
1287
0
          sOrigSrcExtent.minx = 0;
1288
0
          sOrigSrcExtent.maxx = nSrcXSize;
1289
0
          if (sSrcExtent.maxy > sOrigSrcExtent.maxy)
1290
0
            sOrigSrcExtent.maxy = nSrcYSize;
1291
0
          if (sSrcExtent.miny < sOrigSrcExtent.miny)
1292
0
            sOrigSrcExtent.miny = 0;
1293
0
        }
1294
0
      }
1295
0
      memcpy(&sSrcExtent, &sOrigSrcExtent, sizeof(sOrigSrcExtent));
1296
0
      bSuccess = TRUE;
1297
0
    }
1298
0
  }
1299
  /* -------------------------------------------------------------------- */
1300
  /*      If the transformation failed, it is likely that we have such    */
1301
  /*      broad extents that the projection transformation failed at      */
1302
  /*      points around the extents.  If so, we will assume we need       */
1303
  /*      the whole raster.  This and later assumptions are likely to     */
1304
  /*      result in the raster being loaded at a higher resolution        */
1305
  /*      than really needed but should give decent results.              */
1306
  /* -------------------------------------------------------------------- */
1307
0
  if (!bSuccess) {
1308
0
    if (layer->debug) {
1309
0
      if (CSLFetchBoolean(layer->processing, "LOAD_WHOLE_IMAGE", FALSE))
1310
0
        msDebug("msResampleGDALToMap(): "
1311
0
                "LOAD_WHOLE_IMAGE set, loading whole image.\n");
1312
0
      else
1313
0
        msDebug(
1314
0
            "msTransformMapToSource(): "
1315
0
            "pj_transform() failed.  Out of bounds?  Loading whole image.\n");
1316
0
    }
1317
1318
0
    sSrcExtent.minx = 0;
1319
0
    sSrcExtent.maxx = nSrcXSize;
1320
0
    sSrcExtent.miny = 0;
1321
0
    sSrcExtent.maxy = nSrcYSize;
1322
0
  }
1323
1324
  /* -------------------------------------------------------------------- */
1325
  /*      If requesting at the raster resolution, on pixel boundaries,    */
1326
  /*      and no reprojection is  involved, we don't need any resampling. */
1327
  /*      And if they match an integral subsampling factor, no need to    */
1328
  /*      add pixel margin.                                               */
1329
  /*      This optimization helps a lot when operating with mode=tile     */
1330
  /*      and that the underlying raster is tiled and share the same      */
1331
  /*      tiling scheme as the queried tile mode.                         */
1332
  /* -------------------------------------------------------------------- */
1333
0
#define IS_ALMOST_INTEGER(x, eps) (fabs((x) - (int)((x) + 0.5)) < (eps))
1334
1335
0
  if (adfSrcGeoTransform[1] > 0.0 && adfSrcGeoTransform[2] == 0.0 &&
1336
0
      adfSrcGeoTransform[4] == 0.0 && adfSrcGeoTransform[5] < 0.0 &&
1337
0
      IS_ALMOST_INTEGER(sSrcExtent.minx, 0.1) &&
1338
0
      IS_ALMOST_INTEGER(sSrcExtent.miny, 0.1) &&
1339
0
      IS_ALMOST_INTEGER(sSrcExtent.maxx, 0.1) &&
1340
0
      IS_ALMOST_INTEGER(sSrcExtent.maxy, 0.1) &&
1341
0
      !msProjectionsDiffer(&(map->projection), &(layer->projection))) {
1342
0
    double dfXFactor, dfYFactor;
1343
1344
0
    sSrcExtent.minx = (int)(sSrcExtent.minx + 0.5);
1345
0
    sSrcExtent.miny = (int)(sSrcExtent.miny + 0.5);
1346
0
    sSrcExtent.maxx = (int)(sSrcExtent.maxx + 0.5);
1347
0
    sSrcExtent.maxy = (int)(sSrcExtent.maxy + 0.5);
1348
1349
0
    if ((int)(sSrcExtent.maxx - sSrcExtent.minx + 0.5) == nDstXSize &&
1350
0
        (int)(sSrcExtent.maxy - sSrcExtent.miny + 0.5) == nDstYSize) {
1351
0
      if (layer->debug)
1352
0
        msDebug("msResampleGDALToMap(): Request matching raster resolution and "
1353
0
                "pixel boundaries. "
1354
0
                "No need to do resampling/reprojection.\n");
1355
0
      msFree(mask_rb);
1356
0
      return msDrawRasterLayerGDAL(map, layer, image, rb, hDS);
1357
0
    }
1358
1359
0
    dfXFactor = (sSrcExtent.maxx - sSrcExtent.minx) / nDstXSize;
1360
0
    dfYFactor = (sSrcExtent.maxy - sSrcExtent.miny) / nDstYSize;
1361
0
    if (IS_ALMOST_INTEGER(dfXFactor, 1e-5) &&
1362
0
        IS_ALMOST_INTEGER(dfYFactor, 1e-5) &&
1363
0
        IS_ALMOST_INTEGER(sSrcExtent.minx / dfXFactor, 0.1) &&
1364
0
        IS_ALMOST_INTEGER(sSrcExtent.miny / dfXFactor, 0.1) &&
1365
0
        IS_ALMOST_INTEGER(sSrcExtent.maxx / dfYFactor, 0.1) &&
1366
0
        IS_ALMOST_INTEGER(sSrcExtent.maxy / dfYFactor, 0.1)) {
1367
0
      bAddPixelMargin = MS_FALSE;
1368
0
      if (layer->debug)
1369
0
        msDebug(
1370
0
            "msResampleGDALToMap(): Request matching raster resolution "
1371
0
            "and pixel boundaries matching an integral subsampling factor\n");
1372
0
    }
1373
0
  }
1374
1375
  /* -------------------------------------------------------------------- */
1376
  /*      Project desired extents out by 2 pixels, and then strip to      */
1377
  /*      available data.                                                 */
1378
  /* -------------------------------------------------------------------- */
1379
0
  memcpy(&sOrigSrcExtent, &sSrcExtent, sizeof(sSrcExtent));
1380
1381
0
  if (bAddPixelMargin) {
1382
0
    sSrcExtent.minx = floor(sSrcExtent.minx - 1.0);
1383
0
    sSrcExtent.maxx = ceil(sSrcExtent.maxx + 1.0);
1384
0
    sSrcExtent.miny = floor(sSrcExtent.miny - 1.0);
1385
0
    sSrcExtent.maxy = ceil(sSrcExtent.maxy + 1.0);
1386
0
  }
1387
1388
0
  sSrcExtent.minx = MS_MAX(0, sSrcExtent.minx);
1389
0
  sSrcExtent.maxx = MS_MIN(sSrcExtent.maxx, nSrcXSize);
1390
0
  sSrcExtent.miny = MS_MAX(sSrcExtent.miny, 0);
1391
0
  sSrcExtent.maxy = MS_MIN(sSrcExtent.maxy, nSrcYSize);
1392
1393
0
  if (sSrcExtent.maxx <= sSrcExtent.minx ||
1394
0
      sSrcExtent.maxy <= sSrcExtent.miny) {
1395
0
    if (layer->debug)
1396
0
      msDebug("msResampleGDALToMap(): no overlap ... no result.\n");
1397
0
    msFree(mask_rb);
1398
0
    return 0;
1399
0
  }
1400
1401
  /* -------------------------------------------------------------------- */
1402
  /*      Determine desired oversampling ratio.  Default to 2.0 if not    */
1403
  /*      otherwise set.                                                  */
1404
  /* -------------------------------------------------------------------- */
1405
0
  dfOversampleRatio = 2.0;
1406
1407
0
  if (CSLFetchNameValue(layer->processing, "OVERSAMPLE_RATIO") != NULL) {
1408
0
    dfOversampleRatio =
1409
0
        atof(CSLFetchNameValue(layer->processing, "OVERSAMPLE_RATIO"));
1410
0
  }
1411
1412
  /* -------------------------------------------------------------------- */
1413
  /*      Decide on a resolution to read from the source image at.  We    */
1414
  /*      will operate from full resolution data, if we are requesting    */
1415
  /*      at near to full resolution.  Otherwise we will read the data    */
1416
  /*      at twice the resolution of the eventual map.                    */
1417
  /* -------------------------------------------------------------------- */
1418
0
  dfNominalCellSize = sqrt(adfSrcGeoTransform[1] * adfSrcGeoTransform[1] +
1419
0
                           adfSrcGeoTransform[2] * adfSrcGeoTransform[2]);
1420
1421
  /* Check first that the requested extent is not well beyond than the source */
1422
  /* raster. This might be the case for example if asking to visualize */
1423
  /* -180,-89,180,90 in EPSG:4326 from a raster in Arctic Polar Stereographic */
1424
  /* But restrict that to rasters of modest size, otherwise we may end up */
1425
  /* requesting very large dimensions in other legit reprojection cases */
1426
  /* See https://github.com/mapserver/mapserver/issues/5402 */
1427
0
  if (!(sOrigSrcExtent.minx <= -4 * nSrcXSize &&
1428
0
        sOrigSrcExtent.miny <= -4 * nSrcYSize &&
1429
0
        sOrigSrcExtent.maxx >= 5 * nSrcXSize &&
1430
0
        sOrigSrcExtent.maxy >= 5 * nSrcYSize && nSrcXSize < 4000 &&
1431
0
        nSrcYSize < 4000) &&
1432
0
      (sOrigSrcExtent.maxx - sOrigSrcExtent.minx) >
1433
0
          dfOversampleRatio * nDstXSize &&
1434
0
      !CSLFetchBoolean(layer->processing, "LOAD_FULL_RES_IMAGE", FALSE))
1435
0
    sDummyMap.cellsize =
1436
0
        (dfNominalCellSize * (sOrigSrcExtent.maxx - sOrigSrcExtent.minx)) /
1437
0
        (dfOversampleRatio * nDstXSize);
1438
0
  else
1439
0
    sDummyMap.cellsize = dfNominalCellSize;
1440
1441
0
  nLoadImgXSize = MS_MAX(1, (int)(sSrcExtent.maxx - sSrcExtent.minx) *
1442
0
                                (dfNominalCellSize / sDummyMap.cellsize));
1443
0
  nLoadImgYSize = MS_MAX(1, (int)(sSrcExtent.maxy - sSrcExtent.miny) *
1444
0
                                (dfNominalCellSize / sDummyMap.cellsize));
1445
1446
  /*
1447
  ** Because the previous calculation involved some round off, we need
1448
  ** to fixup the cellsize to ensure the map region represents the whole
1449
  ** RAW_WINDOW (at least in X).  Re: bug 1715.
1450
  */
1451
0
  sDummyMap.cellsize =
1452
0
      ((sSrcExtent.maxx - sSrcExtent.minx) * dfNominalCellSize) / nLoadImgXSize;
1453
1454
0
  if (layer->debug)
1455
0
    msDebug("msResampleGDALToMap in effect: cellsize = %f\n",
1456
0
            sDummyMap.cellsize);
1457
1458
0
  adfSrcGeoTransform[0] += +adfSrcGeoTransform[1] * sSrcExtent.minx +
1459
0
                           adfSrcGeoTransform[2] * sSrcExtent.miny;
1460
0
  adfSrcGeoTransform[1] *= (sDummyMap.cellsize / dfNominalCellSize);
1461
0
  adfSrcGeoTransform[2] *= (sDummyMap.cellsize / dfNominalCellSize);
1462
1463
0
  adfSrcGeoTransform[3] += +adfSrcGeoTransform[4] * sSrcExtent.minx +
1464
0
                           adfSrcGeoTransform[5] * sSrcExtent.miny;
1465
0
  adfSrcGeoTransform[4] *= (sDummyMap.cellsize / dfNominalCellSize);
1466
0
  adfSrcGeoTransform[5] *= (sDummyMap.cellsize / dfNominalCellSize);
1467
1468
  /* In the non-rotated case, make sure that the geotransform exactly */
1469
  /* matches the sSrcExtent, even if that generates non-square pixels (#1715) */
1470
  /* The rotated case should ideally be dealt with, but not for now... */
1471
0
  if (adfSrcGeoTransform[2] == 0 && adfSrcGeoTransform[4] == 0 &&
1472
0
      adfSrcGeoTransform[5] < 0 &&
1473
      /* But do that only if the pixels were square before, otherwise */
1474
      /* this is going to mess with source rasters whose pixels aren't at */
1475
      /* all square (#5445) */
1476
0
      fabs(fabs(adfSrcGeoTransform[1]) - fabs(adfSrcGeoTransform[5])) <
1477
0
          0.01 * fabs(adfSrcGeoTransform[1])) {
1478
0
    adfSrcGeoTransform[1] =
1479
0
        (sSrcExtent.maxx - sSrcExtent.minx) * dfNominalCellSize / nLoadImgXSize;
1480
0
    adfSrcGeoTransform[5] = -(sSrcExtent.maxy - sSrcExtent.miny) *
1481
0
                            dfNominalCellSize / nLoadImgYSize;
1482
0
  }
1483
1484
0
  papszAlteredProcessing = CSLDuplicate(layer->processing);
1485
0
  papszAlteredProcessing = CSLSetNameValue(
1486
0
      papszAlteredProcessing, "RAW_WINDOW",
1487
0
      CPLSPrintf("%d %d %d %d", (int)sSrcExtent.minx, (int)sSrcExtent.miny,
1488
0
                 (int)(sSrcExtent.maxx - sSrcExtent.minx),
1489
0
                 (int)(sSrcExtent.maxy - sSrcExtent.miny)));
1490
1491
  /* -------------------------------------------------------------------- */
1492
  /*      We clone this without referencing it knowing that the           */
1493
  /*      srcImage will take a reference on it.  The sDummyMap is         */
1494
  /*      destroyed off the stack, so the missing map reference is        */
1495
  /*      never a problem.  The image's dereference of the                */
1496
  /*      outputformat during the msFreeImage() calls will result in      */
1497
  /*      the output format being cleaned up.                             */
1498
  /*                                                                      */
1499
  /*      We make a copy so we can easily modify the outputformat used    */
1500
  /*      for the temporary image to include transparentency support.     */
1501
  /* -------------------------------------------------------------------- */
1502
0
  sDummyMap.outputformat = msCloneOutputFormat(image->format);
1503
0
  sDummyMap.width = nLoadImgXSize;
1504
0
  sDummyMap.height = nLoadImgYSize;
1505
0
  sDummyMap.mappath = map->mappath;
1506
0
  sDummyMap.shapepath = map->shapepath;
1507
1508
  /* -------------------------------------------------------------------- */
1509
  /*      If we are working in 256 color GD mode, allocate 0 as the       */
1510
  /*      transparent color on the temporary image so it will be          */
1511
  /*      initialized to see-through.  We pick an arbitrary rgb tuple     */
1512
  /*      as our transparent color, but ensure it is initialized in the    */
1513
  /*      map so that normal transparent avoidance will apply.            */
1514
  /* -------------------------------------------------------------------- */
1515
0
  if (MS_RENDERER_PLUGIN(sDummyMap.outputformat)) {
1516
0
    assert(rb);
1517
0
    msInitializeRendererVTable(sDummyMap.outputformat);
1518
0
    assert(sDummyMap.outputformat->imagemode == MS_IMAGEMODE_RGB ||
1519
0
           sDummyMap.outputformat->imagemode == MS_IMAGEMODE_RGBA);
1520
1521
0
    sDummyMap.outputformat->transparent = MS_TRUE;
1522
0
    sDummyMap.outputformat->imagemode = MS_IMAGEMODE_RGBA;
1523
0
    MS_INIT_COLOR(sDummyMap.imagecolor, -1, -1, -1, 255);
1524
0
  }
1525
1526
  /* -------------------------------------------------------------------- */
1527
  /*      Setup a dummy map object we can use to read from the source     */
1528
  /*      raster, with the newly established extents, and resolution.     */
1529
  /* -------------------------------------------------------------------- */
1530
0
  srcImage = msImageCreate(nLoadImgXSize, nLoadImgYSize, sDummyMap.outputformat,
1531
0
                           NULL, NULL, map->resolution, map->defresolution,
1532
0
                           &(sDummyMap.imagecolor));
1533
1534
0
  if (srcImage == NULL) {
1535
0
    msFree(mask_rb);
1536
0
    return -1; /* msSetError() should have been called already */
1537
0
  }
1538
1539
0
  if (MS_RENDERER_PLUGIN(srcImage->format)) {
1540
0
    psrc_rb = &src_rb;
1541
0
    memset(psrc_rb, 0, sizeof(rasterBufferObj));
1542
0
    if (srcImage->format->vtable->supports_pixel_buffer) {
1543
0
      if (MS_UNLIKELY(MS_FAILURE ==
1544
0
                      srcImage->format->vtable->getRasterBufferHandle(
1545
0
                          srcImage, psrc_rb))) {
1546
0
        msFree(mask_rb);
1547
0
        return -1;
1548
0
      }
1549
0
    } else {
1550
0
      if (MS_UNLIKELY(
1551
0
              MS_FAILURE ==
1552
0
              srcImage->format->vtable->initializeRasterBuffer(
1553
0
                  psrc_rb, nLoadImgXSize, nLoadImgYSize, MS_IMAGEMODE_RGBA))) {
1554
0
        msFree(mask_rb);
1555
0
        return -1;
1556
0
      }
1557
0
    }
1558
0
  }
1559
1560
  /* -------------------------------------------------------------------- */
1561
  /*      Draw into the temporary image.  Temporarily replace the         */
1562
  /*      layer processing directive so that we use our RAW_WINDOW.       */
1563
  /* -------------------------------------------------------------------- */
1564
0
  {
1565
0
    char **papszSavedProcessing = layer->processing;
1566
0
    char *origMask = layer->mask;
1567
0
    layer->mask = NULL;
1568
0
    layer->processing = papszAlteredProcessing;
1569
1570
0
    result = msDrawRasterLayerGDAL(&sDummyMap, layer, srcImage, psrc_rb, hDS);
1571
1572
0
    layer->processing = papszSavedProcessing;
1573
0
    layer->mask = origMask;
1574
0
    CSLDestroy(papszAlteredProcessing);
1575
1576
0
    if (result) {
1577
0
      if (MS_RENDERER_PLUGIN(srcImage->format) &&
1578
0
          !srcImage->format->vtable->supports_pixel_buffer)
1579
0
        msFreeRasterBuffer(psrc_rb);
1580
1581
0
      msFreeImage(srcImage);
1582
0
      msFree(mask_rb);
1583
1584
0
      return result;
1585
0
    }
1586
0
  }
1587
1588
  /* -------------------------------------------------------------------- */
1589
  /*      Setup transformations between our source image, and the         */
1590
  /*      target map image.                                               */
1591
  /* -------------------------------------------------------------------- */
1592
0
  pTCBData = msInitProjTransformer(&(layer->projection), adfSrcGeoTransform,
1593
0
                                   &(map->projection), adfDstGeoTransform);
1594
1595
0
  if (pTCBData == NULL) {
1596
0
    if (layer->debug)
1597
0
      msDebug("msInitProjTransformer() returned NULL.\n");
1598
0
    if (MS_RENDERER_PLUGIN(srcImage->format) &&
1599
0
        !srcImage->format->vtable->supports_pixel_buffer)
1600
0
      msFreeRasterBuffer(psrc_rb);
1601
0
    msFreeImage(srcImage);
1602
0
    msFree(mask_rb);
1603
0
    return MS_PROJERR;
1604
0
  }
1605
1606
  /* -------------------------------------------------------------------- */
1607
  /*      It is cheaper to use linear approximations as long as our       */
1608
  /*      error is modest (less than 0.333 pixels).                       */
1609
  /* -------------------------------------------------------------------- */
1610
0
  pACBData = msInitApproxTransformer(msProjTransformer, pTCBData, 0.333);
1611
1612
0
  if (msProjIsGeographicCRS(&(layer->projection))) {
1613
    /* Does the raster cover a whole 360 deg range ? */
1614
0
    if (nSrcXSize == (int)(adfInvSrcGeoTransform[1] * 360 + 0.5))
1615
0
      bWrapAtLeftRight = MS_TRUE;
1616
0
  }
1617
1618
  /* -------------------------------------------------------------------- */
1619
  /*      Perform the resampling.                                         */
1620
  /* -------------------------------------------------------------------- */
1621
0
  if (EQUAL(resampleMode, "AVERAGE"))
1622
0
    result = msAverageRasterResampler(srcImage, psrc_rb, image, rb,
1623
0
                                      msApproxTransformer, pACBData,
1624
0
                                      layer->debug, mask_rb);
1625
0
  else if (EQUAL(resampleMode, "BILINEAR"))
1626
0
    result = msBilinearRasterResampler(srcImage, psrc_rb, image, rb,
1627
0
                                       msApproxTransformer, pACBData,
1628
0
                                       layer->debug, mask_rb, bWrapAtLeftRight);
1629
0
  else
1630
0
    result = msNearestRasterResampler(srcImage, psrc_rb, image, rb,
1631
0
                                      msApproxTransformer, pACBData,
1632
0
                                      layer->debug, mask_rb, bWrapAtLeftRight);
1633
1634
  /* -------------------------------------------------------------------- */
1635
  /*      cleanup                                                         */
1636
  /* -------------------------------------------------------------------- */
1637
0
  msFree(mask_rb);
1638
0
  if (MS_RENDERER_PLUGIN(srcImage->format) &&
1639
0
      !srcImage->format->vtable->supports_pixel_buffer)
1640
0
    msFreeRasterBuffer(psrc_rb);
1641
0
  msFreeImage(srcImage);
1642
1643
0
  msFreeProjTransformer(pTCBData);
1644
0
  msFreeApproxTransformer(pACBData);
1645
1646
0
  return result;
1647
0
}