• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geographika / mapserver / 17709373190

14 Sep 2025 09:32AM UTC coverage: 41.466% (+0.09%) from 41.375%
17709373190

push

github

geographika
Add index templates

62086 of 149729 relevant lines covered (41.47%)

25036.08 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.85
/src/mapagg.cpp
1
/******************************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  AGG rendering and other AGG related functions.
6
 * Author:   Thomas Bonfort and the MapServer team.
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 1996-2007 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 "mapserver.h"
31
#include "fontcache.h"
32
#include "mapagg.h"
33
#include <assert.h>
34
#include "renderers/agg/include/agg_color_rgba.h"
35
#include "renderers/agg/include/agg_pixfmt_rgba.h"
36
#include "renderers/agg/include/agg_renderer_base.h"
37
#include "renderers/agg/include/agg_renderer_scanline.h"
38
#include "renderers/agg/include/agg_math_stroke.h"
39
#include "renderers/agg/include/agg_scanline_p.h"
40
#include "renderers/agg/include/agg_scanline_u.h"
41
#include "renderers/agg/include/agg_rasterizer_scanline_aa.h"
42
#include "renderers/agg/include/agg_span_pattern_rgba.h"
43
#include "renderers/agg/include/agg_span_allocator.h"
44
#include "renderers/agg/include/agg_span_interpolator_linear.h"
45
#include "renderers/agg/include/agg_pattern_filters_rgba.h"
46
#include "renderers/agg/include/agg_image_accessors.h"
47
#include "renderers/agg/include/agg_conv_stroke.h"
48
#include "renderers/agg/include/agg_conv_dash.h"
49
#include "renderers/agg/include/agg_font_freetype.h"
50
#include "renderers/agg/include/agg_conv_contour.h"
51
#include "renderers/agg/include/agg_ellipse.h"
52
#include "renderers/agg/include/agg_gamma_functions.h"
53
#include "renderers/agg/include/agg_blur.h"
54

55
#include "renderers/agg/include/agg_rasterizer_outline_aa.h"
56
#include "renderers/agg/include/agg_renderer_outline_aa.h"
57
#include "renderers/agg/include/agg_renderer_outline_image.h"
58
#include "renderers/agg/include/agg_span_pattern_rgba.h"
59
#include "renderers/agg/include/agg_span_image_filter_rgba.h"
60
#include "renderers/agg/include/agg_glyph_raster_bin.h"
61
#include "renderers/agg/include/agg_renderer_raster_text.h"
62
#include "renderers/agg/include/agg_path_storage_integer.h"
63

64
#include "renderers/agg/include/agg_conv_clipper.h"
65

66
#include "cpl_conv.h"   // CPLGetConfigOption
67
#include "cpl_string.h" // CPLTestBool
68

69
#ifdef USE_PIXMAN
70
#include <pixman.h>
71
#endif
72

73
#include <limits>
74
#include <memory>
75
#include <new>
76
#include <vector>
77

78
typedef mapserver::order_bgra band_order;
79

80
#define AGG_LINESPACE 1.33
81

82
typedef mapserver::int8u band_type;
83
typedef mapserver::rgba8 color_type;
84
typedef mapserver::pixel32_type pixel_type;
85

86
typedef mapserver::blender_rgba_pre<color_type, band_order> blender_pre;
87
typedef mapserver::comp_op_adaptor_rgba_pre<color_type, band_order>
88
    compop_blender_pre;
89

90
typedef mapserver::pixfmt_alpha_blend_rgba<
91
    blender_pre, mapserver::rendering_buffer, pixel_type>
92
    pixel_format;
93
typedef mapserver::pixfmt_custom_blend_rgba<compop_blender_pre,
94
                                            mapserver::rendering_buffer>
95
    compop_pixel_format;
96
typedef mapserver::rendering_buffer rendering_buffer;
97
typedef mapserver::renderer_base<pixel_format> renderer_base;
98
typedef mapserver::renderer_base<compop_pixel_format> compop_renderer_base;
99
typedef mapserver::renderer_scanline_aa_solid<renderer_base> renderer_scanline;
100
typedef mapserver::renderer_scanline_bin_solid<renderer_base>
101
    renderer_scanline_aliased;
102
typedef mapserver::rasterizer_scanline_aa<> rasterizer_scanline;
103
typedef mapserver::font_engine_freetype_int16 font_engine_type;
104
typedef mapserver::font_cache_manager<font_engine_type> font_manager_type;
105
typedef mapserver::conv_curve<font_manager_type::path_adaptor_type>
106
    font_curve_type;
107
typedef mapserver::glyph_raster_bin<color_type> glyph_gen;
108

109
static const color_type AGG_NO_COLOR = color_type(0, 0, 0, 0);
110

111
#define aggColor(c)                                                            \
112
  mapserver::rgba8_pre((c)->red, (c)->green, (c)->blue, (c)->alpha)
113

114
class aggRendererCache {
3,533✔
115
public:
116
  font_engine_type m_feng;
117
  font_manager_type m_fman;
118
  aggRendererCache() : m_fman(m_feng) {}
3,542✔
119
};
120

121
class AGG2Renderer {
122
public:
123
  std::vector<band_type> buffer{};
124
  rendering_buffer m_rendering_buffer;
125
  pixel_format m_pixel_format;
126
  compop_pixel_format m_compop_pixel_format;
127
  renderer_base m_renderer_base;
128
  compop_renderer_base m_compop_renderer_base;
129
  renderer_scanline m_renderer_scanline;
130
  renderer_scanline_aliased m_renderer_scanline_aliased;
131
  rasterizer_scanline m_rasterizer_aa;
132
  rasterizer_scanline m_rasterizer_aa_gamma;
133
  mapserver::scanline_p8 sl_poly; /*packed scanlines, works faster when the area
134
    is larger than the perimeter, in number of pixels*/
135
  mapserver::scanline_u8 sl_line; /*unpacked scanlines, works faster if the area
136
    is roughly equal to the perimeter, in number of pixels*/
137
  bool use_alpha = false;
138
  std::unique_ptr<mapserver::conv_stroke<line_adaptor>> stroke{};
139
  std::unique_ptr<mapserver::conv_dash<line_adaptor>> dash{};
140
  std::unique_ptr<mapserver::conv_stroke<mapserver::conv_dash<line_adaptor>>>
141
      stroke_dash{};
142
  double default_gamma = 0.0;
143
  mapserver::gamma_linear gamma_function;
144
};
145

146
#define AGG_RENDERER(image) ((AGG2Renderer *)(image)->img.plugin)
147

148
template <class VertexSource>
149
static void applyCJC(VertexSource &stroke, int caps, int joins) {
3,128✔
150
  switch (joins) {
3,128✔
151
  case MS_CJC_ROUND:
152
    stroke.line_join(mapserver::round_join);
153
    break;
154
  case MS_CJC_MITER:
155
    stroke.line_join(mapserver::miter_join);
156
    break;
157
  case MS_CJC_BEVEL:
158
  case MS_CJC_NONE:
159
    stroke.line_join(mapserver::bevel_join);
160
    break;
161
  }
162
  switch (caps) {
3,128✔
163
  case MS_CJC_BUTT:
164
  case MS_CJC_NONE:
165
    stroke.line_cap(mapserver::butt_cap);
166
    break;
167
  case MS_CJC_ROUND:
168
    stroke.line_cap(mapserver::round_cap);
169
    break;
170
  case MS_CJC_SQUARE:
171
    stroke.line_cap(mapserver::square_cap);
172
    break;
173
  }
174
}
3,128✔
175

176
int agg2RenderLine(imageObj *img, shapeObj *p, strokeStyleObj *style) {
11,960✔
177

178
  AGG2Renderer *r = AGG_RENDERER(img);
11,960✔
179
  line_adaptor lines = line_adaptor(p);
180

181
  r->m_rasterizer_aa.reset();
182
  r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero);
183
  if (style->antialiased == MS_FALSE) {
11,960✔
184
    r->m_renderer_scanline_aliased.color(aggColor(style->color));
3✔
185
  } else {
186
    r->m_renderer_scanline.color(aggColor(style->color));
11,957✔
187
  }
188

189
  if (style->patternlength <= 0) {
11,960✔
190
    if (!r->stroke) {
11,787✔
191
      r->stroke.reset(new mapserver::conv_stroke<line_adaptor>(lines));
710✔
192
    } else {
193
      r->stroke->attach(lines);
194
    }
195
    r->stroke->width(style->width);
11,787✔
196
    if (style->width > 1) {
11,787✔
197
      applyCJC(*r->stroke, style->linecap, style->linejoin);
2,971✔
198
    } else {
199
      r->stroke->inner_join(mapserver::inner_bevel);
200
      r->stroke->line_join(mapserver::bevel_join);
201
    }
202
    r->m_rasterizer_aa.add_path(*r->stroke);
11,787✔
203
  } else {
204
    if (!r->dash) {
173✔
205
      r->dash.reset(new mapserver::conv_dash<line_adaptor>(lines));
11✔
206
    } else {
207
      r->dash->remove_all_dashes();
208
      r->dash->dash_start(0.0);
209
      r->dash->attach(lines);
210
    }
211
    if (!r->stroke_dash) {
173✔
212
      r->stroke_dash.reset(
213
          new mapserver::conv_stroke<mapserver::conv_dash<line_adaptor>>(
214
              *r->dash));
11✔
215
    } else {
216
      r->stroke_dash->attach(*r->dash);
217
    }
218
    int patt_length = 0;
219
    for (int i = 0; i < style->patternlength; i += 2) {
352✔
220
      if (i < style->patternlength - 1) {
179✔
221
        r->dash->add_dash(MS_MAX(1, MS_NINT(style->pattern[i])),
179✔
222
                          MS_MAX(1, MS_NINT(style->pattern[i + 1])));
179✔
223
        if (style->patternoffset) {
179✔
224
          patt_length += MS_MAX(1, MS_NINT(style->pattern[i])) +
8✔
225
                         MS_MAX(1, MS_NINT(style->pattern[i + 1]));
4✔
226
        }
227
      }
228
    }
229
    if (style->patternoffset > 0) {
173✔
230
      r->dash->dash_start(patt_length - style->patternoffset);
4✔
231
    }
232
    r->stroke_dash->width(style->width);
173✔
233
    if (style->width > 1) {
173✔
234
      applyCJC(*r->stroke_dash, style->linecap, style->linejoin);
157✔
235
    } else {
236
      r->stroke_dash->inner_join(mapserver::inner_bevel);
237
      r->stroke_dash->line_join(mapserver::bevel_join);
238
    }
239
    r->m_rasterizer_aa.add_path(*r->stroke_dash);
173✔
240
  }
241
  if (style->antialiased == MS_FALSE)
11,960✔
242
    mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_line,
3✔
243
                                r->m_renderer_scanline_aliased);
3✔
244
  else
245
    mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_line,
11,957✔
246
                                r->m_renderer_scanline);
11,957✔
247
  return MS_SUCCESS;
11,960✔
248
}
249

250
int agg2RenderLineTiled(imageObj *img, shapeObj *p, imageObj *tile) {
1✔
251

252
  mapserver::pattern_filter_bilinear_rgba8 fltr;
253
  typedef mapserver::line_image_pattern<
254
      mapserver::pattern_filter_bilinear_rgba8>
255
      pattern_type;
256
  typedef mapserver::renderer_outline_image<renderer_base, pattern_type>
257
      renderer_img_type;
258
  typedef mapserver::rasterizer_outline_aa<renderer_img_type,
259
                                           mapserver::line_coord_sat>
260
      rasterizer_img_type;
261
  pattern_type patt(fltr);
262

263
  AGG2Renderer *r = AGG_RENDERER(img);
1✔
264
  AGG2Renderer *tileRenderer = AGG_RENDERER(tile);
1✔
265

266
  line_adaptor lines(p);
267

268
  patt.create(tileRenderer->m_pixel_format);
1✔
269
  renderer_img_type ren_img(r->m_renderer_base, patt);
1✔
270
  rasterizer_img_type ras_img(ren_img);
271
  ras_img.add_path(lines);
1✔
272
  return MS_SUCCESS;
1✔
273
}
1✔
274

275
int agg2RenderPolygon(imageObj *img, shapeObj *p, colorObj *color) {
2,555✔
276
  AGG2Renderer *r = AGG_RENDERER(img);
2,555✔
277
  polygon_adaptor polygons(p);
278
  r->m_rasterizer_aa_gamma.reset();
279
  r->m_rasterizer_aa_gamma.filling_rule(mapserver::fill_even_odd);
280
  r->m_rasterizer_aa_gamma.add_path(polygons);
2,555✔
281
  r->m_renderer_scanline.color(aggColor(color));
2,555✔
282
  mapserver::render_scanlines(r->m_rasterizer_aa_gamma, r->sl_poly,
2,555✔
283
                              r->m_renderer_scanline);
2,555✔
284
  return MS_SUCCESS;
2,555✔
285
}
286

287
static inline double int26p6_to_dbl(int p) { return double(p) / 64.0; }
331,988✔
288

289
template <class PathStorage>
290
bool decompose_ft_outline(const FT_Outline &outline, bool flip_y,
17,181✔
291
                          const mapserver::trans_affine &mtx,
292
                          PathStorage &path) {
293
  double x1, y1, x2, y2, x3, y3;
294

295
  FT_Vector *point;
296
  FT_Vector *limit;
297

298
  unsigned n;     // index of contour in outline
299
  unsigned first; // index of first point in contour
300
  char tag;       // current point's state
301

302
  first = 0;
303

304
  for (n = 0; n < (unsigned)outline.n_contours; n++) {
40,513✔
305
    int last; // index of last point in contour
306

307
    last = outline.contours[n];
23,332✔
308
    limit = outline.points + last;
23,332✔
309

310
    FT_Vector v_start = outline.points[first];
23,332✔
311

312
    FT_Vector v_control = v_start;
313

314
    point = outline.points + first;
315
    auto tags = outline.tags + first;
23,332✔
316
    tag = FT_CURVE_TAG(tags[0]);
23,332✔
317

318
    // A contour cannot start with a cubic control point!
319
    if (tag == FT_CURVE_TAG_CUBIC)
23,332✔
320
      return false;
321

322
    // check first point to determine origin
323
    if (tag == FT_CURVE_TAG_CONIC) {
23,332✔
324
      const FT_Vector v_last = outline.points[last];
10✔
325

326
      // first point is conic control.  Yes, this happens.
327
      if (FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON) {
10✔
328
        // start at last point if it is on the curve
329
        v_start = v_last;
330
        limit--;
2✔
331
      } else {
332
        // if both first and last points are conic,
333
        // start at their middle and record its position
334
        // for closure
335
        v_start.x = (v_start.x + v_last.x) / 2;
8✔
336
        v_start.y = (v_start.y + v_last.y) / 2;
8✔
337
      }
338
      point--;
10✔
339
      tags--;
10✔
340
    }
341

342
    x1 = int26p6_to_dbl(v_start.x);
23,332✔
343
    y1 = int26p6_to_dbl(v_start.y);
23,332✔
344
    if (flip_y)
23,332✔
345
      y1 = -y1;
23,332✔
346
    mtx.transform(&x1, &y1);
347
    path.move_to(x1, y1);
348

349
    while (point < limit) {
237,322✔
350
      point++;
225,645✔
351
      tags++;
225,645✔
352

353
      tag = FT_CURVE_TAG(tags[0]);
225,645✔
354
      switch (tag) {
225,645✔
355
      case FT_CURVE_TAG_ON: // emit a single line_to
120,128✔
356
      {
357
        x1 = int26p6_to_dbl(point->x);
120,128✔
358
        y1 = int26p6_to_dbl(point->y);
120,128✔
359
        if (flip_y)
120,128✔
360
          y1 = -y1;
120,128✔
361
        mtx.transform(&x1, &y1);
362
        path.line_to(x1, y1);
363
        // path.line_to(conv(point->x), flip_y ? -conv(point->y) :
364
        // conv(point->y));
365
        continue;
120,128✔
366
      }
367

368
      case FT_CURVE_TAG_CONIC: // consume conic arcs
105,517✔
369
      {
370
        v_control.x = point->x;
105,517✔
371
        v_control.y = point->y;
105,517✔
372

373
      Do_Conic:
188,528✔
374
        if (point < limit) {
188,528✔
375
          FT_Vector vec;
376
          FT_Vector v_middle;
377

378
          point++;
176,873✔
379
          tags++;
176,873✔
380
          tag = FT_CURVE_TAG(tags[0]);
176,873✔
381

382
          vec.x = point->x;
176,873✔
383
          vec.y = point->y;
176,873✔
384

385
          if (tag == FT_CURVE_TAG_ON) {
270,735✔
386
            x1 = int26p6_to_dbl(v_control.x);
93,862✔
387
            y1 = int26p6_to_dbl(v_control.y);
93,862✔
388
            x2 = int26p6_to_dbl(vec.x);
93,862✔
389
            y2 = int26p6_to_dbl(vec.y);
93,862✔
390
            if (flip_y) {
93,862✔
391
              y1 = -y1;
93,862✔
392
              y2 = -y2;
93,862✔
393
            }
394
            mtx.transform(&x1, &y1);
395
            mtx.transform(&x2, &y2);
396
            path.curve3(x1, y1, x2, y2);
93,862✔
397
            continue;
398
          }
399

400
          if (tag != FT_CURVE_TAG_CONIC)
83,011✔
401
            return false;
402

403
          v_middle.x = (v_control.x + vec.x) / 2;
83,011✔
404
          v_middle.y = (v_control.y + vec.y) / 2;
83,011✔
405

406
          x1 = int26p6_to_dbl(v_control.x);
83,011✔
407
          y1 = int26p6_to_dbl(v_control.y);
83,011✔
408
          x2 = int26p6_to_dbl(v_middle.x);
83,011✔
409
          y2 = int26p6_to_dbl(v_middle.y);
83,011✔
410
          if (flip_y) {
83,011✔
411
            y1 = -y1;
83,011✔
412
            y2 = -y2;
83,011✔
413
          }
414
          mtx.transform(&x1, &y1);
415
          mtx.transform(&x2, &y2);
416
          path.curve3(x1, y1, x2, y2);
83,011✔
417

418
          // path.curve3(conv(v_control.x),
419
          //             flip_y ? -conv(v_control.y) : conv(v_control.y),
420
          //             conv(v_middle.x),
421
          //             flip_y ? -conv(v_middle.y) : conv(v_middle.y));
422

423
          v_control = vec;
424
          goto Do_Conic;
83,011✔
425
        }
426

427
        x1 = int26p6_to_dbl(v_control.x);
11,655✔
428
        y1 = int26p6_to_dbl(v_control.y);
11,655✔
429
        x2 = int26p6_to_dbl(v_start.x);
430
        y2 = int26p6_to_dbl(v_start.y);
431
        if (flip_y) {
11,655✔
432
          y1 = -y1;
11,655✔
433
          y2 = -y2;
434
        }
435
        mtx.transform(&x1, &y1);
436
        mtx.transform(&x2, &y2);
437
        path.curve3(x1, y1, x2, y2);
11,655✔
438

439
        // path.curve3(conv(v_control.x),
440
        //             flip_y ? -conv(v_control.y) : conv(v_control.y),
441
        //             conv(v_start.x),
442
        //             flip_y ? -conv(v_start.y) : conv(v_start.y));
443
        goto Close;
11,655✔
444
      }
445

446
      default: // FT_CURVE_TAG_CUBIC
×
447
      {
448
        FT_Vector vec1, vec2;
449

450
        if (point + 1 > limit || FT_CURVE_TAG(tags[1]) != FT_CURVE_TAG_CUBIC) {
×
451
          return false;
452
        }
453

454
        vec1.x = point[0].x;
×
455
        vec1.y = point[0].y;
×
456
        vec2.x = point[1].x;
×
457
        vec2.y = point[1].y;
×
458

459
        point += 2;
×
460
        tags += 2;
×
461

462
        if (point <= limit) {
×
463
          FT_Vector vec;
464

465
          vec.x = point->x;
×
466
          vec.y = point->y;
×
467

468
          x1 = int26p6_to_dbl(vec1.x);
×
469
          y1 = int26p6_to_dbl(vec1.y);
×
470
          x2 = int26p6_to_dbl(vec2.x);
×
471
          y2 = int26p6_to_dbl(vec2.y);
×
472
          x3 = int26p6_to_dbl(vec.x);
×
473
          y3 = int26p6_to_dbl(vec.y);
×
474
          if (flip_y) {
×
475
            y1 = -y1;
×
476
            y2 = -y2;
×
477
            y3 = -y3;
×
478
          }
479
          mtx.transform(&x1, &y1);
480
          mtx.transform(&x2, &y2);
481
          mtx.transform(&x3, &y3);
482
          path.curve4(x1, y1, x2, y2, x3, y3);
×
483

484
          // path.curve4(conv(vec1.x),
485
          //             flip_y ? -conv(vec1.y) : conv(vec1.y),
486
          //             conv(vec2.x),
487
          //             flip_y ? -conv(vec2.y) : conv(vec2.y),
488
          //             conv(vec.x),
489
          //             flip_y ? -conv(vec.y) : conv(vec.y));
490
          continue;
491
        }
×
492

493
        x1 = int26p6_to_dbl(vec1.x);
×
494
        y1 = int26p6_to_dbl(vec1.y);
×
495
        x2 = int26p6_to_dbl(vec2.x);
×
496
        y2 = int26p6_to_dbl(vec2.y);
×
497
        x3 = int26p6_to_dbl(v_start.x);
498
        y3 = int26p6_to_dbl(v_start.y);
499
        if (flip_y) {
×
500
          y1 = -y1;
×
501
          y2 = -y2;
×
502
          y3 = -y3;
503
        }
504
        mtx.transform(&x1, &y1);
505
        mtx.transform(&x2, &y2);
506
        mtx.transform(&x3, &y3);
507
        path.curve4(x1, y1, x2, y2, x3, y3);
×
508

509
        // path.curve4(conv(vec1.x),
510
        //             flip_y ? -conv(vec1.y) : conv(vec1.y),
511
        //             conv(vec2.x),
512
        //             flip_y ? -conv(vec2.y) : conv(vec2.y),
513
        //             conv(v_start.x),
514
        //             flip_y ? -conv(v_start.y) : conv(v_start.y));
515
        goto Close;
×
516
      }
517
      }
120,128✔
518
    }
519

520
    path.close_polygon();
521

522
  Close:
23,332✔
523
    first = last + 1;
23,332✔
524
  }
525

526
  return true;
527
}
528

529
int agg2RenderPolygonTiled(imageObj *img, shapeObj *p, imageObj *tile) {
65✔
530
  assert(img->format->renderer == tile->format->renderer);
531

532
  AGG2Renderer *r = AGG_RENDERER(img);
65✔
533
  AGG2Renderer *tileRenderer = AGG_RENDERER(tile);
65✔
534
  polygon_adaptor polygons(p);
535
  typedef mapserver::wrap_mode_repeat wrap_type;
536
  typedef mapserver::image_accessor_wrap<pixel_format, wrap_type, wrap_type>
537
      img_source_type;
538
  typedef mapserver::span_pattern_rgba<img_source_type> span_gen_type;
539
  mapserver::span_allocator<mapserver::rgba8> sa;
540
  r->m_rasterizer_aa.reset();
541
  r->m_rasterizer_aa.filling_rule(mapserver::fill_even_odd);
542
  img_source_type img_src(tileRenderer->m_pixel_format);
65✔
543
  span_gen_type sg(img_src, 0, 0);
544
  r->m_rasterizer_aa.add_path(polygons);
65✔
545
  mapserver::render_scanlines_aa(r->m_rasterizer_aa, r->sl_poly,
65✔
546
                                 r->m_renderer_base, sa, sg);
65✔
547
  return MS_SUCCESS;
65✔
548
}
549

550
int agg2RenderGlyphsPath(imageObj *img, const textSymbolObj *ts, colorObj *c,
2,358✔
551
                         colorObj *oc, int ow, int /*isMarker*/) {
552

553
  const textPathObj *tp = ts->textpath;
2,358✔
554
  mapserver::path_storage glyphs;
555
  mapserver::trans_affine trans;
556
  AGG2Renderer *r = AGG_RENDERER(img);
2,358✔
557
  r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero);
558
  for (int i = 0; i < tp->numglyphs; i++) {
19,539✔
559
    glyphObj *gl = tp->glyphs + i;
17,181✔
560
    trans.reset();
17,181✔
561
    trans.rotate(-gl->rot);
17,181✔
562
    trans.translate(gl->pnt.x, gl->pnt.y);
17,181✔
563
    outline_element *ol = msGetGlyphOutline(gl->face, gl->glyph);
17,181✔
564
    if (!ol) {
17,181✔
565
      return MS_FAILURE;
566
    }
567
    decompose_ft_outline(ol->outline, true, trans, glyphs);
17,181✔
568
  }
569
  mapserver::conv_curve<mapserver::path_storage> m_curves(glyphs);
2,358✔
570
  if (oc) {
2,358✔
571
    r->m_rasterizer_aa.reset();
572
    r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero);
573
    mapserver::conv_contour<mapserver::conv_curve<mapserver::path_storage>> cc(
574
        m_curves);
575
    cc.width(ow + 1);
443✔
576
    r->m_rasterizer_aa.add_path(cc);
443✔
577
    r->m_renderer_scanline.color(aggColor(oc));
443✔
578
    mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_line,
443✔
579
                                r->m_renderer_scanline);
443✔
580
  }
581
  if (c) {
2,358✔
582
    r->m_rasterizer_aa.reset();
583
    r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero);
584
    r->m_rasterizer_aa.add_path(m_curves);
2,358✔
585
    r->m_renderer_scanline.color(aggColor(c));
2,358✔
586
    mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_line,
2,358✔
587
                                r->m_renderer_scanline);
2,358✔
588
  }
589
  return MS_SUCCESS;
590
}
591

592
mapserver::path_storage imageVectorSymbol(symbolObj *symbol) {
6,433✔
593
  mapserver::path_storage path;
594
  int is_new = 1;
595

596
  for (int i = 0; i < symbol->numpoints; i++) {
52,832✔
597
    if ((symbol->points[i].x == -99) && (symbol->points[i].y == -99))
46,399✔
598
      is_new = 1;
599

600
    else {
601
      if (is_new) {
45,565✔
602
        path.move_to(symbol->points[i].x, symbol->points[i].y);
7,267✔
603
        is_new = 0;
604
      } else {
605
        path.line_to(symbol->points[i].x, symbol->points[i].y);
38,298✔
606
      }
607
    }
608
  }
609
  return path;
6,433✔
610
}
611

612
int agg2RenderVectorSymbol(imageObj *img, double x, double y, symbolObj *symbol,
6,432✔
613
                           symbolStyleObj *style) {
614
  AGG2Renderer *r = AGG_RENDERER(img);
6,432✔
615
  double ox = symbol->sizex * 0.5;
6,432✔
616
  double oy = symbol->sizey * 0.5;
6,432✔
617

618
  mapserver::path_storage path = imageVectorSymbol(symbol);
6,432✔
619
  mapserver::trans_affine mtx;
620
  mtx *= mapserver::trans_affine_translation(-ox, -oy);
6,432✔
621
  mtx *= mapserver::trans_affine_scaling(style->scale);
6,432✔
622
  mtx *= mapserver::trans_affine_rotation(-style->rotation);
12,864✔
623
  mtx *= mapserver::trans_affine_translation(x, y);
×
624
  path.transform(mtx);
6,432✔
625
  if (style->color) {
6,432✔
626
    r->m_rasterizer_aa.reset();
627
    r->m_rasterizer_aa.filling_rule(mapserver::fill_even_odd);
628
    r->m_rasterizer_aa.add_path(path);
4,903✔
629
    r->m_renderer_scanline.color(aggColor(style->color));
4,903✔
630
    mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_poly,
4,903✔
631
                                r->m_renderer_scanline);
4,903✔
632
  }
633
  if (style->outlinecolor) {
6,432✔
634
    r->m_rasterizer_aa.reset();
635
    r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero);
636
    r->m_renderer_scanline.color(aggColor(style->outlinecolor));
1,588✔
637
    mapserver::conv_stroke<mapserver::path_storage> stroke(path);
638
    stroke.width(style->outlinewidth);
1,588✔
639
    r->m_rasterizer_aa.add_path(stroke);
1,588✔
640
    mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_poly,
1,588✔
641
                                r->m_renderer_scanline);
1,588✔
642
  }
643
  return MS_SUCCESS;
6,432✔
644
}
645

646
int agg2RenderPixmapSymbol(imageObj *img, double x, double y, symbolObj *symbol,
423✔
647
                           symbolStyleObj *style) {
648
  AGG2Renderer *r = AGG_RENDERER(img);
423✔
649
  rasterBufferObj *pixmap = symbol->pixmap_buffer;
423✔
650
  assert(pixmap->type == MS_BUFFER_BYTE_RGBA);
651
  rendering_buffer b(pixmap->data.rgba.pixels, pixmap->width, pixmap->height,
652
                     pixmap->data.rgba.row_step);
423✔
653
  pixel_format pf(b);
654

655
  r->m_rasterizer_aa.reset();
656
  r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero);
657
  if ((style->rotation != 0 && style->rotation != MS_PI * 2.) ||
423✔
658
      style->scale != 1) {
398✔
659
    mapserver::trans_affine image_mtx;
660
    image_mtx *= mapserver::trans_affine_translation(-(pf.width() / 2.),
116✔
661
                                                     -(pf.height() / 2.));
58✔
662
    /*agg angles are antitrigonometric*/
663
    image_mtx *= mapserver::trans_affine_rotation(-style->rotation);
58✔
664
    image_mtx *= mapserver::trans_affine_scaling(style->scale);
58✔
665

666
    image_mtx *= mapserver::trans_affine_translation(x, y);
58✔
667
    image_mtx.invert();
58✔
668
    typedef mapserver::span_interpolator_linear<> interpolator_type;
669
    interpolator_type interpolator(image_mtx);
670
    mapserver::span_allocator<color_type> sa;
671

672
    // "hardcoded" bilinear filter
673
    //------------------------------------------
674
    typedef mapserver::span_image_filter_rgba_bilinear_clip<pixel_format,
675
                                                            interpolator_type>
676
        span_gen_type;
677
    span_gen_type sg(pf, mapserver::rgba(0, 0, 0, 0), interpolator);
678
    mapserver::path_storage pixmap_bbox;
679
    int ims_2 =
680
        MS_NINT(MS_MAX(pixmap->width, pixmap->height) * style->scale * 1.415) /
58✔
681
            2 +
58✔
682
        1;
58✔
683

684
    pixmap_bbox.move_to(x - ims_2, y - ims_2);
58✔
685
    pixmap_bbox.line_to(x + ims_2, y - ims_2);
58✔
686
    pixmap_bbox.line_to(x + ims_2, y + ims_2);
58✔
687
    pixmap_bbox.line_to(x - ims_2, y + ims_2);
688

689
    r->m_rasterizer_aa.add_path(pixmap_bbox);
58✔
690
    mapserver::render_scanlines_aa(r->m_rasterizer_aa, r->sl_poly,
58✔
691
                                   r->m_renderer_base, sa, sg);
58✔
692
  } else {
58✔
693
    // just copy the image at the correct location (we place the pixmap on
694
    // the nearest integer pixel to avoid blurring)
695
    unsigned alpha = mapserver::cover_full;
696
    if (style && style->color) {
365✔
697
      alpha = style->color->alpha;
2✔
698
    }
699
    r->m_renderer_base.blend_from(pf, 0, MS_NINT(x - pixmap->width / 2.),
365✔
700
                                  MS_NINT(y - pixmap->height / 2.), alpha);
365✔
701
  }
702
  return MS_SUCCESS;
423✔
703
}
704

705
int agg2RenderEllipseSymbol(imageObj *image, double x, double y,
8,969✔
706
                            symbolObj *symbol, symbolStyleObj *style) {
707
  AGG2Renderer *r = AGG_RENDERER(image);
8,969✔
708
  mapserver::path_storage path;
709
  mapserver::ellipse ellipse(x, y, symbol->sizex * style->scale / 2,
8,969✔
710
                             symbol->sizey * style->scale / 2);
8,969✔
711
  path.concat_path(ellipse);
8,969✔
712
  if (style->rotation != 0) {
8,969✔
713
    mapserver::trans_affine mtx;
714
    mtx *= mapserver::trans_affine_translation(-x, -y);
5✔
715
    /*agg angles are antitrigonometric*/
716
    mtx *= mapserver::trans_affine_rotation(-style->rotation);
10✔
717
    mtx *= mapserver::trans_affine_translation(x, y);
×
718
    path.transform(mtx);
5✔
719
  }
720

721
  if (style->color) {
8,969✔
722
    r->m_rasterizer_aa.reset();
723
    r->m_rasterizer_aa.filling_rule(mapserver::fill_even_odd);
724
    r->m_rasterizer_aa.add_path(path);
8,669✔
725
    r->m_renderer_scanline.color(aggColor(style->color));
8,669✔
726
    mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_line,
8,669✔
727
                                r->m_renderer_scanline);
8,669✔
728
  }
729
  if (style->outlinewidth) {
8,969✔
730
    r->m_rasterizer_aa.reset();
731
    r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero);
732
    mapserver::conv_stroke<mapserver::path_storage> stroke(path);
733
    stroke.width(style->outlinewidth);
429✔
734
    r->m_rasterizer_aa.add_path(stroke);
429✔
735
    r->m_renderer_scanline.color(aggColor(style->outlinecolor));
429✔
736
    mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_poly,
429✔
737
                                r->m_renderer_scanline);
429✔
738
  }
739
  return MS_SUCCESS;
8,969✔
740
}
741

742
int agg2RenderTile(imageObj * /*img*/, imageObj * /*tile*/, double /*x*/,
×
743
                   double /*y*/) {
744
  /*
745
  AGG2Renderer *imgRenderer = agg2GetRenderer(img);
746
  AGG2Renderer *tileRenderer = agg2GetRenderer(tile);
747
  */
748
  return MS_FAILURE;
×
749
}
750

751
int aggInitializeRasterBuffer(rasterBufferObj *rb, int width, int height,
171✔
752
                              int mode) {
753
  rb->type = MS_BUFFER_BYTE_RGBA;
171✔
754
  rb->data.rgba.pixel_step = 4;
171✔
755
  rb->data.rgba.row_step = rb->data.rgba.pixel_step * width;
171✔
756
  rb->width = width;
171✔
757
  rb->height = height;
171✔
758
  int nBytes = rb->data.rgba.row_step * height;
171✔
759
  rb->data.rgba.pixels = (band_type *)msSmallCalloc(nBytes, sizeof(band_type));
171✔
760
  rb->data.rgba.r = &(rb->data.rgba.pixels[band_order::R]);
171✔
761
  rb->data.rgba.g = &(rb->data.rgba.pixels[band_order::G]);
171✔
762
  rb->data.rgba.b = &(rb->data.rgba.pixels[band_order::B]);
171✔
763
  if (mode == MS_IMAGEMODE_RGBA) {
171✔
764
    rb->data.rgba.a = &(rb->data.rgba.pixels[band_order::A]);
171✔
765
  }
766
  return MS_SUCCESS;
171✔
767
}
768

769
int aggGetRasterBufferHandle(imageObj *img, rasterBufferObj *rb) {
1,449✔
770
  AGG2Renderer *r = AGG_RENDERER(img);
1,449✔
771
  rb->type = MS_BUFFER_BYTE_RGBA;
1,449✔
772
  rb->data.rgba.pixels = r->buffer.data();
1,449✔
773
  rb->data.rgba.row_step = r->m_rendering_buffer.stride();
1,449✔
774
  rb->data.rgba.pixel_step = 4;
1,449✔
775
  rb->width = r->m_rendering_buffer.width();
1,449✔
776
  rb->height = r->m_rendering_buffer.height();
1,449✔
777
  rb->data.rgba.r = &(r->buffer[band_order::R]);
1,449✔
778
  rb->data.rgba.g = &(r->buffer[band_order::G]);
1,449✔
779
  rb->data.rgba.b = &(r->buffer[band_order::B]);
1,449✔
780
  if (r->use_alpha)
1,449✔
781
    rb->data.rgba.a = &(r->buffer[band_order::A]);
368✔
782
  else
783
    rb->data.rgba.a = NULL;
1,081✔
784
  return MS_SUCCESS;
1,449✔
785
}
786

787
int aggGetRasterBufferCopy(imageObj *img, rasterBufferObj *rb) {
171✔
788
  AGG2Renderer *r = AGG_RENDERER(img);
171✔
789
  aggInitializeRasterBuffer(rb, img->width, img->height, MS_IMAGEMODE_RGBA);
171✔
790
  int nBytes = r->m_rendering_buffer.stride() * r->m_rendering_buffer.height();
171✔
791
  memcpy(rb->data.rgba.pixels, r->buffer.data(), nBytes);
171✔
792
  return MS_SUCCESS;
171✔
793
}
794

795
int agg2MergeRasterBuffer(imageObj *dest, rasterBufferObj *overlay,
11✔
796
                          double opacity, int srcX, int srcY, int dstX,
797
                          int dstY, int width, int height) {
798
  assert(overlay->type == MS_BUFFER_BYTE_RGBA);
799
  rendering_buffer b(overlay->data.rgba.pixels, overlay->width, overlay->height,
800
                     overlay->data.rgba.row_step);
11✔
801
  pixel_format pf(b);
802
  AGG2Renderer *r = AGG_RENDERER(dest);
11✔
803
  mapserver::rect_base<int> src_rect(srcX, srcY, srcX + width, srcY + height);
11✔
804
  r->m_renderer_base.blend_from(pf, &src_rect, dstX - srcX, dstY - srcY,
11✔
805
                                unsigned(opacity * 255));
11✔
806
  return MS_SUCCESS;
11✔
807
}
808

809
/* image i/o */
810
imageObj *agg2CreateImage(int width, int height, outputFormatObj *format,
1,353✔
811
                          colorObj *bg) {
812
  imageObj *image = NULL;
813
  if (format->imagemode != MS_IMAGEMODE_RGB &&
1,353✔
814
      format->imagemode != MS_IMAGEMODE_RGBA) {
815
    msSetError(MS_MISCERR,
×
816
               "AGG2 driver only supports RGB or RGBA pixel models.",
817
               "agg2CreateImage()");
818
    return image;
×
819
  }
820
  if (width > 0 && height > 0) {
1,353✔
821
    image = (imageObj *)calloc(1, sizeof(imageObj));
1,353✔
822
    MS_CHECK_ALLOC(image, sizeof(imageObj), NULL);
1,353✔
823
    AGG2Renderer *r = new AGG2Renderer();
1,353✔
824

825
    /* Compute size on 64bit and check that it is compatible of the platform
826
     * size_t */
827
    const AGG_INT64U bufSize64 =
1,353✔
828
        (AGG_INT64U)width * height * 4 * sizeof(band_type);
1,353✔
829
    if (bufSize64 > std::numeric_limits<size_t>::max() / 2) {
1,353✔
830
      msSetError(MS_MEMERR,
×
831
                 "%s: %d: Out of memory allocating " AGG_INT64U_FRMT
832
                 " bytes.\n",
833
                 "agg2CreateImage()", __FILE__, __LINE__, bufSize64);
834
      free(image);
×
835
      delete r;
×
836
      return NULL;
×
837
    }
838

839
    const size_t bufSize = (size_t)bufSize64;
840
    try {
841
      r->buffer.resize(bufSize / sizeof(band_type));
1,353✔
842
    } catch (const std::bad_alloc &) {
×
843
      msSetError(MS_MEMERR,
×
844
                 "%s: %d: Out of memory allocating " AGG_INT64U_FRMT
845
                 " bytes.\n",
846
                 "agg2CreateImage()", __FILE__, __LINE__, bufSize64);
847
      free(image);
×
848
      delete r;
×
849
      return NULL;
850
    }
×
851
    r->m_rendering_buffer.attach(r->buffer.data(), width, height, width * 4);
1,353✔
852
    r->m_pixel_format.attach(r->m_rendering_buffer);
1,353✔
853
    r->m_compop_pixel_format.attach(r->m_rendering_buffer);
854
    r->m_renderer_base.attach(r->m_pixel_format);
1,353✔
855
    r->m_compop_renderer_base.attach(r->m_compop_pixel_format);
1,353✔
856
    r->m_renderer_scanline.attach(r->m_renderer_base);
1,353✔
857
    r->m_renderer_scanline_aliased.attach(r->m_renderer_base);
858
    r->default_gamma = atof(msGetOutputFormatOption(format, "GAMMA", "0.75"));
1,353✔
859
    if (r->default_gamma <= 0.0 || r->default_gamma >= 1.0) {
1,353✔
860
      r->default_gamma = 0.75;
2✔
861
    }
862
    r->gamma_function.set(0, r->default_gamma);
1,353✔
863
    r->m_rasterizer_aa_gamma.gamma(r->gamma_function);
1,353✔
864
    if (bg && !format->transparent)
1,353✔
865
      r->m_renderer_base.clear(aggColor(bg));
933✔
866
    else
867
      r->m_renderer_base.clear(AGG_NO_COLOR);
420✔
868

869
    if (!bg || format->transparent || format->imagemode == MS_IMAGEMODE_RGBA) {
1,353✔
870
      r->use_alpha = true;
424✔
871
    } else {
872
      r->use_alpha = false;
929✔
873
    }
874
    image->img.plugin = (void *)r;
1,353✔
875
  } else {
876
    msSetError(MS_RENDERERERR, "Cannot create cairo image of size %dx%d.",
×
877
               "msImageCreateCairo()", width, height);
878
  }
879

880
  return image;
881
}
882

883
int agg2SaveImage(imageObj * /*img*/, mapObj * /*map*/, FILE * /*fp*/,
×
884
                  outputFormatObj * /*format*/) {
885

886
  return MS_FAILURE;
×
887
}
888

889
int agg2StartNewLayer(imageObj *img, mapObj * /*map*/, layerObj *layer) {
1,760✔
890
  AGG2Renderer *r = AGG_RENDERER(img);
1,760✔
891
  const char *sgamma = msLayerGetProcessingKey(layer, "GAMMA");
1,760✔
892
  double gamma;
893
  if (sgamma) {
1,760✔
894
    gamma = atof(sgamma);
895
    if (gamma <= 0 || gamma >= 1)
4✔
896
      gamma = 0.75;
897
  } else {
898
    gamma = r->default_gamma;
1,756✔
899
  }
900
  if (r->gamma_function.end() != gamma) {
1,760✔
901
    r->gamma_function.end(gamma);
902
    r->m_rasterizer_aa_gamma.gamma(r->gamma_function);
×
903
  }
904
  return MS_SUCCESS;
1,760✔
905
}
906

907
int agg2CloseNewLayer(imageObj * /*img*/, mapObj * /*map*/,
1,760✔
908
                      layerObj * /*layer*/) {
909
  return MS_SUCCESS;
1,760✔
910
}
911

912
int agg2FreeImage(imageObj *image) {
1,353✔
913
  AGG2Renderer *r = AGG_RENDERER(image);
1,353✔
914
  delete r;
1,353✔
915
  image->img.plugin = NULL;
1,353✔
916
  return MS_SUCCESS;
1,353✔
917
}
918

919
int agg2FreeSymbol(symbolObj * /*symbol*/) { return MS_SUCCESS; }
1,189✔
920

921
int agg2InitCache(void **vcache) {
3,542✔
922
  aggRendererCache *cache = new aggRendererCache();
3,542✔
923
  *vcache = (void *)cache;
3,542✔
924
  return MS_SUCCESS;
3,542✔
925
}
926

927
int agg2Cleanup(void *vcache) {
3,533✔
928
  aggRendererCache *cache = (aggRendererCache *)vcache;
929
  delete cache;
7,066✔
930
  return MS_SUCCESS;
3,533✔
931
}
932

933
// ------------------------------------------------------------------------
934
// Function to create a custom hatch symbol based on an arbitrary angle.
935
// ------------------------------------------------------------------------
936
static mapserver::path_storage createHatch(double ox, double oy, double rx,
112✔
937
                                           double ry, int sx, int sy,
938
                                           double angle, double step) {
939
  mapserver::path_storage path;
940
  // restrict the angle to [0 180[, i.e ]-pi/2,pi/2] in radians
941
  angle = fmod(angle, 360.0);
112✔
942
  if (angle < 0)
112✔
943
    angle += 360;
×
944
  if (angle >= 180)
112✔
945
    angle -= 180;
16✔
946

947
  // treat 2 easy cases which would cause divide by 0 in generic case
948
  if (angle == 0) {
112✔
949
    double y0 = step - fmod(oy - ry, step);
32✔
950
    if ((oy - ry) < 0) {
32✔
951
      y0 -= step;
16✔
952
    }
953
    for (double y = y0; y < sy; y += step) {
128✔
954
      path.move_to(0, y);
955
      path.line_to(sx, y);
96✔
956
    }
957
    return path;
958
  }
959
  if (angle == 90) {
80✔
960
    double x0 = step - fmod(ox - rx, step);
16✔
961
    if ((ox - rx) < 0) {
16✔
962
      x0 -= step;
12✔
963
    }
964
    for (double x = x0; x < sx; x += step) {
128✔
965
      path.move_to(x, 0);
966
      path.line_to(x, sy);
112✔
967
    }
968
    return path;
969
  }
970

971
  double theta = (90 - angle) * MS_DEG_TO_RAD; /* theta in ]-pi/2 , pi/2] */
64✔
972
  double ct = cos(theta);
64✔
973
  double st = sin(theta);
64✔
974
  double invct = 1.0 / ct;
64✔
975
  double invst = 1.0 / st;
64✔
976
  double
977
      r0; /* distance from first hatch line to the top-left (if angle in 0,pi/2)
978
               or bottom-left (if angle in -pi/2,0) corner of the hatch bbox */
979
  double rmax = sqrt((
64✔
980
      double)(sx * sx +
64✔
981
              sy *
64✔
982
                  sy)); /* distance to the furthest hatch we will have to create
983
TODO: this could be optimized for bounding boxes where width is very different
984
than height for certain hatch angles */
985
  double rref =
64✔
986
      rx * ct + ry * st; /* distance to the line passing through the refpoint,
64✔
987
                            origin is (0,0) of the imageObj */
988
  double
989
      rcorner; /* distance to the line passing through the topleft or bottomleft
990
                  corner of the hatch bbox (origin is (0,0) of imageObj) */
991

992
  /* calculate the distance from the refpoint to the top right of the path */
993
  if (angle < 90) {
64✔
994
    rcorner = ox * ct + oy * st;
48✔
995
    r0 = step - fmod(rcorner - rref, step);
48✔
996
    if (rcorner - rref < 0)
48✔
997
      r0 -= step;
36✔
998
  } else {
999
    rcorner = ox * ct + (oy + sy) * st;
16✔
1000
    r0 = step - fmod(rcorner - rref, step);
16✔
1001
    if (rcorner - rref < 0)
16✔
1002
      r0 -= step;
12✔
1003
    st = -st;
16✔
1004
    invst = -invst;
16✔
1005
  }
1006

1007
  // parametrize each line as r = x.cos(theta) + y.sin(theta)
1008
  for (double r = r0; r < rmax; r += step) {
408✔
1009
    int inter = 0;
1010
    double x, y;
1011
    double pt[4]; // array to store the coordinates of intersection of the line
1012
                  // with the sides
1013
    // in the general case there will only be two intersections
1014
    // so pt[4] should be sufficient to store the coordinates of the
1015
    // intersection, but we allocate pt[8] to treat the special and
1016
    // rare/unfortunate case when the line is a perfect diagonal (and therefore
1017
    // intersects all four sides) note that the order for testing is important
1018
    // in this case so that the first two intersection points actually
1019
    // correspond to the diagonal and not a degenerate line
1020

1021
    // test for intersection with each side
1022

1023
    y = r * invst;
344✔
1024
    x = 0; // test for intersection with left of image
1025
    if (y >= 0 && y <= sy) {
344✔
1026
      pt[2 * inter] = x;
112✔
1027
      pt[2 * inter + 1] = y;
112✔
1028
      inter++;
1029
    }
1030
    x = sx;
344✔
1031
    y = (r - sx * ct) * invst; // test for intersection with right of image
344✔
1032
    if (y >= 0 && y <= sy) {
344✔
1033
      pt[2 * inter] = x;
102✔
1034
      pt[2 * inter + 1] = y;
102✔
1035
      inter++;
102✔
1036
    }
1037
    if (inter < 2) {
118✔
1038
      y = 0;
1039
      x = r * invct; // test for intersection with top of image
344✔
1040
      if (x >= 0 && x <= sx) {
344✔
1041
        pt[2 * inter] = x;
226✔
1042
        pt[2 * inter + 1] = y;
226✔
1043
        inter++;
226✔
1044
      }
1045
    }
1046
    if (inter < 2) {
344✔
1047
      y = sy;
232✔
1048
      x = (r - sy * st) * invct; // test for intersection with bottom of image
232✔
1049
      if (x >= 0 && x <= sx) {
232✔
1050
        pt[2 * inter] = x;
216✔
1051
        pt[2 * inter + 1] = y;
216✔
1052
        inter++;
216✔
1053
      }
1054
    }
1055
    if (inter == 2 && (pt[0] != pt[2] || pt[1] != pt[3])) {
344✔
1056
      // the line intersects with two sides of the image, it should therefore be
1057
      // drawn
1058
      if (angle < 90) {
328✔
1059
        path.move_to(pt[0], pt[1]);
216✔
1060
        path.line_to(pt[2], pt[3]);
216✔
1061
      } else {
1062
        path.move_to(pt[0], sy - pt[1]);
112✔
1063
        path.line_to(pt[2], sy - pt[3]);
112✔
1064
      }
1065
    }
1066
  }
1067
  return path;
1068
}
1069

1070
template <class VertexSource>
1071
int renderPolygonHatches(imageObj *img, VertexSource &clipper,
112✔
1072
                         colorObj *color) {
1073
  if (img->format->renderer == MS_RENDER_WITH_AGG) {
112✔
1074
    AGG2Renderer *r = AGG_RENDERER(img);
28✔
1075
    r->m_rasterizer_aa_gamma.reset();
1076
    r->m_rasterizer_aa_gamma.filling_rule(mapserver::fill_non_zero);
1077
    r->m_rasterizer_aa_gamma.add_path(clipper);
28✔
1078
    r->m_renderer_scanline.color(aggColor(color));
28✔
1079
    mapserver::render_scanlines(r->m_rasterizer_aa_gamma, r->sl_poly,
28✔
1080
                                r->m_renderer_scanline);
28✔
1081
  } else {
1082
    shapeObj shape;
1083
    msInitShape(&shape);
84✔
1084
    int allocated = 20;
1085
    lineObj line;
1086
    shape.line = &line;
84✔
1087
    shape.numlines = 1;
84✔
1088
    shape.line[0].point =
84✔
1089
        (pointObj *)msSmallCalloc(allocated, sizeof(pointObj));
84✔
1090
    shape.line[0].numpoints = 0;
84✔
1091
    double x = 0, y = 0;
84✔
1092
    unsigned int cmd;
1093
    clipper.rewind(0);
84✔
1094
    while ((cmd = clipper.vertex(&x, &y)) != mapserver::path_cmd_stop) {
2,486✔
1095
      switch (cmd) {
2,402✔
1096
      case mapserver::path_cmd_line_to:
1,454✔
1097
        if (shape.line[0].numpoints == allocated) {
1,454✔
1098
          allocated *= 2;
×
1099
          shape.line[0].point = (pointObj *)msSmallRealloc(
×
1100
              shape.line[0].point, allocated * sizeof(pointObj));
×
1101
        }
1102
        shape.line[0].point[shape.line[0].numpoints].x = x;
1,454✔
1103
        shape.line[0].point[shape.line[0].numpoints].y = y;
1,454✔
1104
        shape.line[0].numpoints++;
1,454✔
1105
        break;
1,454✔
1106
      case mapserver::path_cmd_move_to:
474✔
1107
        shape.line[0].point[0].x = x;
474✔
1108
        shape.line[0].point[0].y = y;
474✔
1109
        shape.line[0].numpoints = 1;
474✔
1110
        break;
474✔
1111
      case mapserver::path_cmd_end_poly | mapserver::path_flags_close:
474✔
1112
        if (shape.line[0].numpoints > 2) {
474✔
1113
          if (MS_UNLIKELY(MS_FAILURE == MS_IMAGE_RENDERER(img)->renderPolygon(
474✔
1114
                                            img, &shape, color))) {
1115
            free(shape.line[0].point);
×
1116
            return MS_FAILURE;
×
1117
          }
1118
        }
1119
        break;
1120
      default:
1121
        assert(0); // WTF?
1122
      }
1123
    }
1124
    free(shape.line[0].point);
84✔
1125
  }
1126
  return MS_SUCCESS;
1127
}
1128

1129
int msHatchPolygon(imageObj *img, shapeObj *poly, double spacing, double width,
112✔
1130
                   double *pattern, int patternlength, double angle,
1131
                   colorObj *color) {
1132
  assert(MS_RENDERER_PLUGIN(img->format));
1133
  msComputeBounds(poly);
112✔
1134

1135
  /* amount we should expand the bounding box by */
1136
  double exp = width * 0.7072;
112✔
1137

1138
  /* width and height of the bounding box we will be creating the hatch in */
1139
  int pw = (int)(poly->bounds.maxx - poly->bounds.minx + exp * 2) + 1;
112✔
1140
  int ph = (int)(poly->bounds.maxy - poly->bounds.miny + exp * 2) + 1;
112✔
1141

1142
  /* position of the top-left corner of the bounding box */
1143
  double ox = poly->bounds.minx - exp;
112✔
1144
  double oy = poly->bounds.miny - exp;
112✔
1145

1146
  // create a rectangular hatch of size pw,ph starting at 0,0
1147
  // the created hatch is of the size of the shape's bounding box
1148
  mapserver::path_storage hatch =
1149
      createHatch(ox, oy, img->refpt.x, img->refpt.y, pw, ph, angle, spacing);
112✔
1150
  if (hatch.total_vertices() <= 0)
112✔
1151
    return MS_SUCCESS;
1152

1153
  // translate the hatch so it overlaps the current shape
1154
  hatch.transform(mapserver::trans_affine_translation(ox, oy));
112✔
1155

1156
  polygon_adaptor polygons(poly);
1157

1158
  if (patternlength > 1) {
112✔
1159
    // dash the color-hatch and render it clipped by the shape
1160
    mapserver::conv_dash<mapserver::path_storage> dash(hatch);
1161
    mapserver::conv_stroke<mapserver::conv_dash<mapserver::path_storage>>
1162
        stroke(dash);
1163
    for (int i = 0; i < patternlength; i += 2) {
48✔
1164
      if (i < patternlength - 1) {
32✔
1165
        dash.add_dash(pattern[i], pattern[i + 1]);
32✔
1166
      }
1167
    }
1168
    stroke.width(width);
1169
    stroke.line_cap(mapserver::butt_cap);
1170
    mapserver::conv_clipper<
1171
        polygon_adaptor,
1172
        mapserver::conv_stroke<mapserver::conv_dash<mapserver::path_storage>>>
1173
        clipper(polygons, stroke, mapserver::clipper_and);
16✔
1174
    renderPolygonHatches(img, clipper, color);
16✔
1175
  } else {
16✔
1176
    // render the hatch clipped by the shape
1177
    mapserver::conv_stroke<mapserver::path_storage> stroke(hatch);
1178
    stroke.width(width);
1179
    stroke.line_cap(mapserver::butt_cap);
1180
    mapserver::conv_clipper<polygon_adaptor,
1181
                            mapserver::conv_stroke<mapserver::path_storage>>
1182
        clipper(polygons, stroke, mapserver::clipper_and);
96✔
1183
    renderPolygonHatches(img, clipper, color);
96✔
1184
  }
96✔
1185

1186
  // assert(prevCmd == mapserver::path_cmd_line_to);
1187
  // delete lines;
1188
  return MS_SUCCESS;
1189
}
1190

1191
#ifdef USE_PIXMAN
1192
static pixman_op_t ms2pixman_compop(CompositingOperation c) {
1193
  switch (c) {
1194
  case MS_COMPOP_CLEAR:
1195
    return PIXMAN_OP_CLEAR;
1196
  case MS_COMPOP_SRC:
1197
    return PIXMAN_OP_SRC;
1198
  case MS_COMPOP_DST:
1199
    return PIXMAN_OP_DST;
1200
  case MS_COMPOP_SRC_OVER:
1201
    return PIXMAN_OP_OVER;
1202
  case MS_COMPOP_DST_OVER:
1203
    return PIXMAN_OP_OVER_REVERSE;
1204
  case MS_COMPOP_SRC_IN:
1205
    return PIXMAN_OP_IN;
1206
  case MS_COMPOP_DST_IN:
1207
    return PIXMAN_OP_IN_REVERSE;
1208
  case MS_COMPOP_SRC_OUT:
1209
    return PIXMAN_OP_OUT;
1210
  case MS_COMPOP_DST_OUT:
1211
    return PIXMAN_OP_OUT_REVERSE;
1212
  case MS_COMPOP_SRC_ATOP:
1213
    return PIXMAN_OP_ATOP;
1214
  case MS_COMPOP_DST_ATOP:
1215
    return PIXMAN_OP_ATOP_REVERSE;
1216
  case MS_COMPOP_XOR:
1217
    return PIXMAN_OP_XOR;
1218
  case MS_COMPOP_PLUS:
1219
    return PIXMAN_OP_ADD;
1220
  case MS_COMPOP_MULTIPLY:
1221
    return PIXMAN_OP_MULTIPLY;
1222
  case MS_COMPOP_SCREEN:
1223
    return PIXMAN_OP_SCREEN;
1224
  case MS_COMPOP_OVERLAY:
1225
    return PIXMAN_OP_OVERLAY;
1226
  case MS_COMPOP_DARKEN:
1227
    return PIXMAN_OP_DARKEN;
1228
  case MS_COMPOP_LIGHTEN:
1229
    return PIXMAN_OP_LIGHTEN;
1230
  case MS_COMPOP_COLOR_DODGE:
1231
    return PIXMAN_OP_COLOR_DODGE;
1232
  case MS_COMPOP_COLOR_BURN:
1233
    return PIXMAN_OP_COLOR_DODGE;
1234
  case MS_COMPOP_HARD_LIGHT:
1235
    return PIXMAN_OP_HARD_LIGHT;
1236
  case MS_COMPOP_SOFT_LIGHT:
1237
    return PIXMAN_OP_SOFT_LIGHT;
1238
  case MS_COMPOP_DIFFERENCE:
1239
    return PIXMAN_OP_DIFFERENCE;
1240
  case MS_COMPOP_EXCLUSION:
1241
    return PIXMAN_OP_EXCLUSION;
1242
  case MS_COMPOP_HSL_HUE:
1243
    return PIXMAN_OP_HSL_HUE;
1244
  case MS_COMPOP_HSL_LUMINOSITY:
1245
    return PIXMAN_OP_HSL_LUMINOSITY;
1246
  case MS_COMPOP_HSL_SATURATION:
1247
    return PIXMAN_OP_HSL_SATURATION;
1248
  case MS_COMPOP_HSL_COLOR:
1249
    return PIXMAN_OP_HSL_COLOR;
1250
  case MS_COMPOP_INVERT:
1251
  case MS_COMPOP_INVERT_RGB:
1252
  case MS_COMPOP_MINUS:
1253
  case MS_COMPOP_CONTRAST:
1254
  default:
1255
    return PIXMAN_OP_OVER;
1256
  }
1257
}
1258
#endif
1259

1260
static mapserver::comp_op_e ms2agg_compop(CompositingOperation c) {
1261
  switch (c) {
1262
  case MS_COMPOP_CLEAR:
1263
    return mapserver::comp_op_clear;
1264
  case MS_COMPOP_SRC:
1265
    return mapserver::comp_op_src;
1266
  case MS_COMPOP_DST:
1267
    return mapserver::comp_op_dst;
1268
  case MS_COMPOP_SRC_OVER:
1269
    return mapserver::comp_op_src_over;
1270
  case MS_COMPOP_DST_OVER:
1271
    return mapserver::comp_op_dst_over;
1272
  case MS_COMPOP_SRC_IN:
1273
    return mapserver::comp_op_src_in;
1274
  case MS_COMPOP_DST_IN:
1275
    return mapserver::comp_op_dst_in;
1276
  case MS_COMPOP_SRC_OUT:
1277
    return mapserver::comp_op_src_out;
1278
  case MS_COMPOP_DST_OUT:
1279
    return mapserver::comp_op_dst_out;
1280
  case MS_COMPOP_SRC_ATOP:
1281
    return mapserver::comp_op_src_atop;
1282
  case MS_COMPOP_DST_ATOP:
1283
    return mapserver::comp_op_dst_atop;
1284
  case MS_COMPOP_XOR:
1285
    return mapserver::comp_op_xor;
1286
  case MS_COMPOP_PLUS:
1287
    return mapserver::comp_op_plus;
1288
  case MS_COMPOP_MINUS:
1289
    return mapserver::comp_op_minus;
1290
  case MS_COMPOP_MULTIPLY:
1291
    return mapserver::comp_op_multiply;
1292
  case MS_COMPOP_SCREEN:
1293
    return mapserver::comp_op_screen;
1294
  case MS_COMPOP_OVERLAY:
1295
    return mapserver::comp_op_overlay;
1296
  case MS_COMPOP_DARKEN:
1297
    return mapserver::comp_op_darken;
1298
  case MS_COMPOP_LIGHTEN:
1299
    return mapserver::comp_op_lighten;
1300
  case MS_COMPOP_COLOR_DODGE:
1301
    return mapserver::comp_op_color_dodge;
1302
  case MS_COMPOP_COLOR_BURN:
1303
    return mapserver::comp_op_color_burn;
1304
  case MS_COMPOP_HARD_LIGHT:
1305
    return mapserver::comp_op_hard_light;
1306
  case MS_COMPOP_SOFT_LIGHT:
1307
    return mapserver::comp_op_soft_light;
1308
  case MS_COMPOP_DIFFERENCE:
1309
    return mapserver::comp_op_difference;
1310
  case MS_COMPOP_EXCLUSION:
1311
    return mapserver::comp_op_exclusion;
1312
  case MS_COMPOP_CONTRAST:
1313
    return mapserver::comp_op_contrast;
1314
  case MS_COMPOP_INVERT:
1315
    return mapserver::comp_op_invert;
1316
  case MS_COMPOP_INVERT_RGB:
1317
    return mapserver::comp_op_invert_rgb;
1318
  case MS_COMPOP_HSL_HUE:
1319
    return mapserver::comp_op_hsl_hue;
1320
  case MS_COMPOP_HSL_LUMINOSITY:
1321
    return mapserver::comp_op_hsl_luminosity;
1322
  case MS_COMPOP_HSL_SATURATION:
1323
    return mapserver::comp_op_hsl_saturation;
1324
  case MS_COMPOP_HSL_COLOR:
1325
    return mapserver::comp_op_hsl_color;
1326
  default:
1327
    return mapserver::comp_op_src_over;
1328
  }
1329
}
1330

1331
#ifdef USE_PIXMAN
1332
static int aggCompositeRasterBufferPixman(imageObj *dest,
1333
                                          rasterBufferObj *overlay,
1334
                                          CompositingOperation comp,
1335
                                          int opacity) {
1336
  assert(overlay->type == MS_BUFFER_BYTE_RGBA);
1337
  AGG2Renderer *r = AGG_RENDERER(dest);
1338
  pixman_image_t *si = pixman_image_create_bits(
1339
      PIXMAN_a8r8g8b8, overlay->width, overlay->height,
1340
      (uint32_t *)overlay->data.rgba.pixels, overlay->data.rgba.row_step);
1341
  pixman_image_t *bi = pixman_image_create_bits(
1342
      PIXMAN_a8r8g8b8, dest->width, dest->height,
1343
      reinterpret_cast<uint32_t *>(&(r->buffer[0])), dest->width * 4);
1344
  pixman_image_t *alpha_mask_i = NULL, *alpha_mask_i_ptr;
1345
  pixman_image_set_filter(si, PIXMAN_FILTER_NEAREST, NULL, 0);
1346
  unsigned char *alpha_mask = NULL;
1347
  if (opacity > 0) {
1348
    if (opacity == 100) {
1349
      alpha_mask_i_ptr = NULL;
1350
    } else {
1351
      unsigned char alpha = (unsigned char)(MS_NINT(opacity * 2.55));
1352
      if (!alpha_mask_i) {
1353
        alpha_mask = (unsigned char *)msSmallMalloc(dest->width * dest->height);
1354
        alpha_mask_i =
1355
            pixman_image_create_bits(PIXMAN_a8, dest->width, dest->height,
1356
                                     (uint32_t *)alpha_mask, dest->width);
1357
      }
1358
      memset(alpha_mask, alpha, dest->width * dest->height);
1359
      alpha_mask_i_ptr = alpha_mask_i;
1360
    }
1361
    pixman_image_composite(ms2pixman_compop(comp), si, alpha_mask_i_ptr, bi, 0,
1362
                           0, 0, 0, 0, 0, dest->width, dest->height);
1363
  }
1364
  pixman_image_unref(si);
1365
  pixman_image_unref(bi);
1366
  if (alpha_mask_i) {
1367
    pixman_image_unref(alpha_mask_i);
1368
    msFree(alpha_mask);
1369
  }
1370
  return MS_SUCCESS;
1371
}
1372
#endif
1373

1374
static int aggCompositeRasterBufferNoPixman(imageObj *dest,
32✔
1375
                                            rasterBufferObj *overlay,
1376
                                            CompositingOperation comp,
1377
                                            int opacity) {
1378
  assert(overlay->type == MS_BUFFER_BYTE_RGBA);
1379
  AGG2Renderer *r = AGG_RENDERER(dest);
32✔
1380
  rendering_buffer b(overlay->data.rgba.pixels, overlay->width, overlay->height,
1381
                     overlay->data.rgba.row_step);
32✔
1382
  pixel_format pf(b);
1383
  mapserver::comp_op_e comp_op = ms2agg_compop(comp);
1384
  if (comp_op == mapserver::comp_op_src_over) {
32✔
1385
    r->m_renderer_base.blend_from(pf, 0, 0, 0,
26✔
1386
                                  unsigned(MS_NINT(opacity * 2.55)));
26✔
1387
  } else {
1388
    compop_pixel_format pixf(r->m_rendering_buffer);
6✔
1389
    compop_renderer_base ren(pixf);
1390
    pixf.comp_op(comp_op);
1391
    ren.blend_from(pf, 0, 0, 0, unsigned(MS_NINT(opacity * 2.55)));
6✔
1392
  }
1393
  return MS_SUCCESS;
32✔
1394
}
1395

1396
void msApplyBlurringCompositingFilter(rasterBufferObj *rb,
×
1397
                                      unsigned int radius) {
1398
  rendering_buffer b(rb->data.rgba.pixels, rb->width, rb->height,
1399
                     rb->data.rgba.row_step);
×
1400
  pixel_format pf(b);
1401
  mapserver::stack_blur_rgba32(pf, radius, radius);
×
1402
}
×
1403

1404
int msPopulateRendererVTableAGG(rendererVTableObj *renderer) {
3,542✔
1405
  renderer->compositeRasterBuffer = &aggCompositeRasterBufferNoPixman;
3,542✔
1406
#ifdef USE_PIXMAN
1407
  const char *pszUsePixman = CPLGetConfigOption("MS_USE_PIXMAN", "YES");
1408
  if (CPLTestBool(pszUsePixman)) {
1409
    renderer->compositeRasterBuffer = &aggCompositeRasterBufferPixman;
1410
  }
1411
#endif
1412
  renderer->supports_pixel_buffer = 1;
3,542✔
1413
  renderer->use_imagecache = 0;
3,542✔
1414
  renderer->supports_clipping = 0;
3,542✔
1415
  renderer->supports_svg = 0;
3,542✔
1416
  renderer->default_transform_mode = MS_TRANSFORM_SIMPLIFY;
3,542✔
1417
  agg2InitCache(&(MS_RENDERER_CACHE(renderer)));
3,542✔
1418
  renderer->cleanup = agg2Cleanup;
3,542✔
1419
  renderer->renderLine = &agg2RenderLine;
3,542✔
1420

1421
  renderer->renderPolygon = &agg2RenderPolygon;
3,542✔
1422
  renderer->renderPolygonTiled = &agg2RenderPolygonTiled;
3,542✔
1423
  renderer->renderLineTiled = &agg2RenderLineTiled;
3,542✔
1424

1425
  renderer->renderGlyphs = &agg2RenderGlyphsPath;
3,542✔
1426

1427
  renderer->renderVectorSymbol = &agg2RenderVectorSymbol;
3,542✔
1428

1429
  renderer->renderPixmapSymbol = &agg2RenderPixmapSymbol;
3,542✔
1430

1431
  renderer->renderEllipseSymbol = &agg2RenderEllipseSymbol;
3,542✔
1432

1433
  renderer->renderTile = &agg2RenderTile;
3,542✔
1434

1435
  renderer->getRasterBufferHandle = &aggGetRasterBufferHandle;
3,542✔
1436
  renderer->getRasterBufferCopy = aggGetRasterBufferCopy;
3,542✔
1437
  renderer->initializeRasterBuffer = aggInitializeRasterBuffer;
3,542✔
1438

1439
  renderer->mergeRasterBuffer = &agg2MergeRasterBuffer;
3,542✔
1440
  renderer->loadImageFromFile = msLoadMSRasterBufferFromFile;
3,542✔
1441
  renderer->createImage = &agg2CreateImage;
3,542✔
1442
  renderer->saveImage = &agg2SaveImage;
3,542✔
1443

1444
  renderer->startLayer = &agg2StartNewLayer;
3,542✔
1445
  renderer->endLayer = &agg2CloseNewLayer;
3,542✔
1446

1447
  renderer->freeImage = &agg2FreeImage;
3,542✔
1448
  renderer->freeSymbol = &agg2FreeSymbol;
3,542✔
1449

1450
  return MS_SUCCESS;
3,542✔
1451
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc