Coverage Report

Created: 2025-06-13 06:29

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