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 #ifdef USE_PIXMAN
67 #include <pixman.h>
68 #endif
69 
70 #ifdef AGG_ALIASED_ENABLED
71 #include "renderers/agg/include/agg_renderer_primitives.h"
72 #include "renderers/agg/include/agg_rasterizer_outline.h"
73 #endif
74 
75 typedef mapserver::order_bgra band_order;
76 
77 #define AGG_LINESPACE 1.33
78 
79 typedef mapserver::int8u band_type;
80 typedef mapserver::rgba8 color_type;
81 typedef mapserver::pixel32_type pixel_type;
82 
83 typedef mapserver::blender_rgba_pre<color_type, band_order> blender_pre;
84 typedef mapserver::comp_op_adaptor_rgba_pre<color_type, band_order> compop_blender_pre;
85 
86 typedef mapserver::pixfmt_alpha_blend_rgba<blender_pre, mapserver::rendering_buffer, pixel_type> pixel_format;
87 typedef mapserver::pixfmt_custom_blend_rgba<compop_blender_pre, mapserver::rendering_buffer> compop_pixel_format;
88 typedef mapserver::rendering_buffer rendering_buffer;
89 typedef mapserver::renderer_base<pixel_format> renderer_base;
90 typedef mapserver::renderer_base<compop_pixel_format> compop_renderer_base;
91 typedef mapserver::renderer_scanline_aa_solid<renderer_base> renderer_scanline;
92 typedef mapserver::rasterizer_scanline_aa<> rasterizer_scanline;
93 typedef mapserver::font_engine_freetype_int16 font_engine_type;
94 typedef mapserver::font_cache_manager<font_engine_type> font_manager_type;
95 typedef mapserver::conv_curve<font_manager_type::path_adaptor_type> font_curve_type;
96 typedef mapserver::glyph_raster_bin<color_type> glyph_gen;
97 
98 #ifdef AGG_ALIASED_ENABLED
99 typedef mapserver::renderer_primitives<renderer_base> renderer_primitives;
100 typedef mapserver::rasterizer_outline<renderer_primitives> rasterizer_outline;
101 #endif
102 static color_type AGG_NO_COLOR = color_type(0, 0, 0, 0);
103 
104 #define aggColor(c) mapserver::rgba8_pre((c)->red, (c)->green, (c)->blue, (c)->alpha)
105 
106 class aggRendererCache
107 {
108 public:
109   font_engine_type m_feng;
110   font_manager_type m_fman;
aggRendererCache()111   aggRendererCache(): m_fman(m_feng) {}
112 };
113 
114 class AGG2Renderer
115 {
116 public:
117 
AGG2Renderer()118   AGG2Renderer()
119 #ifdef AGG_ALIASED_ENABLED
120     :
121     m_renderer_primitives(m_renderer_base),
122     m_rasterizer_primitives(m_renderer_primitives)
123 #endif
124   {
125     stroke = NULL;
126     dash = NULL;
127     stroke_dash = NULL;
128   }
129 
~AGG2Renderer()130   ~AGG2Renderer() {
131     if(stroke) {
132       delete stroke;
133     }
134     if(dash) {
135       delete dash;
136     }
137     if(stroke_dash) {
138       delete stroke_dash;
139     }
140   }
141 
142   band_type* buffer;
143   rendering_buffer m_rendering_buffer;
144   pixel_format m_pixel_format;
145   compop_pixel_format m_compop_pixel_format;
146   renderer_base m_renderer_base;
147   compop_renderer_base m_compop_renderer_base;
148   renderer_scanline m_renderer_scanline;
149 #ifdef AGG_ALIASED_ENABLED
150   renderer_primitives m_renderer_primitives;
151   rasterizer_outline m_rasterizer_primitives;
152 #endif
153   rasterizer_scanline m_rasterizer_aa;
154   rasterizer_scanline m_rasterizer_aa_gamma;
155   mapserver::scanline_p8 sl_poly; /*packed scanlines, works faster when the area is larger
156     than the perimeter, in number of pixels*/
157   mapserver::scanline_u8 sl_line; /*unpacked scanlines, works faster if the area is roughly
158     equal to the perimeter, in number of pixels*/
159   bool use_alpha;
160   mapserver::conv_stroke<line_adaptor> *stroke;
161   mapserver::conv_dash<line_adaptor> *dash;
162   mapserver::conv_stroke<mapserver::conv_dash<line_adaptor> > *stroke_dash;
163   double default_gamma;
164   mapserver::gamma_linear gamma_function;
165 };
166 
167 #define AGG_RENDERER(image) ((AGG2Renderer*) (image)->img.plugin)
168 
169 template<class VertexSource>
applyCJC(VertexSource & stroke,int caps,int joins)170 static void applyCJC(VertexSource &stroke, int caps, int joins)
171 {
172   switch (joins) {
173     case MS_CJC_ROUND:
174       stroke.line_join(mapserver::round_join);
175       break;
176     case MS_CJC_MITER:
177       stroke.line_join(mapserver::miter_join);
178       break;
179     case MS_CJC_BEVEL:
180     case MS_CJC_NONE:
181       stroke.line_join(mapserver::bevel_join);
182       break;
183   }
184   switch (caps) {
185     case MS_CJC_BUTT:
186     case MS_CJC_NONE:
187       stroke.line_cap(mapserver::butt_cap);
188       break;
189     case MS_CJC_ROUND:
190       stroke.line_cap(mapserver::round_cap);
191       break;
192     case MS_CJC_SQUARE:
193       stroke.line_cap(mapserver::square_cap);
194       break;
195   }
196 }
197 
agg2RenderLine(imageObj * img,shapeObj * p,strokeStyleObj * style)198 int agg2RenderLine(imageObj *img, shapeObj *p, strokeStyleObj *style)
199 {
200 
201   AGG2Renderer *r = AGG_RENDERER(img);
202   line_adaptor lines = line_adaptor(p);
203 
204 #ifdef AGG_ALIASED_ENABLED
205   r->m_rasterizer_primitives.reset();
206   r->m_renderer_primitives.line_color(aggColor(style->color));
207   r->m_rasterizer_primitives.add_path(lines);
208   return MS_SUCCESS;
209 #endif
210 
211   r->m_rasterizer_aa.reset();
212   r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero);
213   r->m_renderer_scanline.color(aggColor(style->color));
214 
215   if (style->patternlength <= 0) {
216     if(!r->stroke) {
217       r->stroke = new mapserver::conv_stroke<line_adaptor>(lines);
218     } else {
219       r->stroke->attach(lines);
220     }
221     r->stroke->width(style->width);
222     if(style->width>1) {
223       applyCJC(*r->stroke, style->linecap, style->linejoin);
224     } else {
225       r->stroke->inner_join(mapserver::inner_bevel);
226       r->stroke->line_join(mapserver::bevel_join);
227     }
228     r->m_rasterizer_aa.add_path(*r->stroke);
229   } else {
230     if(!r->dash) {
231       r->dash = new mapserver::conv_dash<line_adaptor>(lines);
232     } else {
233       r->dash->remove_all_dashes();
234       r->dash->dash_start(0.0);
235       r->dash->attach(lines);
236     }
237     if(!r->stroke_dash) {
238       r->stroke_dash = new mapserver::conv_stroke<mapserver::conv_dash<line_adaptor> > (*r->dash);
239     } else {
240       r->stroke_dash->attach(*r->dash);
241     }
242     int patt_length = 0;
243     for (int i = 0; i < style->patternlength; i += 2) {
244       if (i < style->patternlength - 1) {
245         r->dash->add_dash(MS_MAX(1,MS_NINT(style->pattern[i])),
246                       MS_MAX(1,MS_NINT(style->pattern[i + 1])));
247         if(style->patternoffset) {
248           patt_length += MS_MAX(1,MS_NINT(style->pattern[i])) +
249                          MS_MAX(1,MS_NINT(style->pattern[i + 1]));
250         }
251       }
252     }
253     if(style->patternoffset > 0) {
254       r->dash->dash_start(patt_length - style->patternoffset);
255     }
256     r->stroke_dash->width(style->width);
257     if(style->width>1) {
258       applyCJC(*r->stroke_dash, style->linecap, style->linejoin);
259     } else {
260       r->stroke_dash->inner_join(mapserver::inner_bevel);
261       r->stroke_dash->line_join(mapserver::bevel_join);
262     }
263     r->m_rasterizer_aa.add_path(*r->stroke_dash);
264   }
265   mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_line, r->m_renderer_scanline);
266   return MS_SUCCESS;
267 }
268 
agg2RenderLineTiled(imageObj * img,shapeObj * p,imageObj * tile)269 int agg2RenderLineTiled(imageObj *img, shapeObj *p, imageObj * tile)
270 {
271 
272   mapserver::pattern_filter_bilinear_rgba8 fltr;
273   typedef mapserver::line_image_pattern<mapserver::pattern_filter_bilinear_rgba8> pattern_type;
274   typedef mapserver::renderer_outline_image<renderer_base, pattern_type> renderer_img_type;
275   typedef mapserver::rasterizer_outline_aa<renderer_img_type, mapserver::line_coord_sat> rasterizer_img_type;
276   pattern_type patt(fltr);
277 
278   AGG2Renderer *r = AGG_RENDERER(img);
279   AGG2Renderer *tileRenderer = AGG_RENDERER(tile);
280 
281   line_adaptor lines(p);
282 
283   patt.create(tileRenderer->m_pixel_format);
284   renderer_img_type ren_img(r->m_renderer_base, patt);
285   rasterizer_img_type ras_img(ren_img);
286   ras_img.add_path(lines);
287   return MS_SUCCESS;
288 }
289 
agg2RenderPolygon(imageObj * img,shapeObj * p,colorObj * color)290 int agg2RenderPolygon(imageObj *img, shapeObj *p, colorObj * color)
291 {
292   AGG2Renderer *r = AGG_RENDERER(img);
293   polygon_adaptor polygons(p);
294   r->m_rasterizer_aa_gamma.reset();
295   r->m_rasterizer_aa_gamma.filling_rule(mapserver::fill_even_odd);
296   r->m_rasterizer_aa_gamma.add_path(polygons);
297   r->m_renderer_scanline.color(aggColor(color));
298   mapserver::render_scanlines(r->m_rasterizer_aa_gamma, r->sl_poly, r->m_renderer_scanline);
299   return MS_SUCCESS;
300 }
301 
int26p6_to_dbl(int p)302 static inline double int26p6_to_dbl(int p)
303     {
304         return double(p) / 64.0;
305     }
306 
307 template<class PathStorage>
decompose_ft_outline(const FT_Outline & outline,bool flip_y,const mapserver::trans_affine & mtx,PathStorage & path)308     bool decompose_ft_outline(const FT_Outline& outline,
309                               bool flip_y,
310                               const mapserver::trans_affine& mtx,
311                               PathStorage& path)
312     {
313         FT_Vector   v_last;
314         FT_Vector   v_control;
315         FT_Vector   v_start;
316         double x1, y1, x2, y2, x3, y3;
317 
318         FT_Vector*  point;
319         FT_Vector*  limit;
320         char*       tags;
321 
322         int   n;         // index of contour in outline
323         int   first;     // index of first point in contour
324         char  tag;       // current point's state
325 
326         first = 0;
327 
328         for(n = 0; n < outline.n_contours; n++)
329         {
330             int  last;  // index of last point in contour
331 
332             last  = outline.contours[n];
333             limit = outline.points + last;
334 
335             v_start = outline.points[first];
336             v_last  = outline.points[last];
337 
338             v_control = v_start;
339 
340             point = outline.points + first;
341             tags  = outline.tags  + first;
342             tag   = FT_CURVE_TAG(tags[0]);
343 
344             // A contour cannot start with a cubic control point!
345             if(tag == FT_CURVE_TAG_CUBIC) return false;
346 
347             // check first point to determine origin
348             if( tag == FT_CURVE_TAG_CONIC)
349             {
350                 // first point is conic control.  Yes, this happens.
351                 if(FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON)
352                 {
353                     // start at last point if it is on the curve
354                     v_start = v_last;
355                     limit--;
356                 }
357                 else
358                 {
359                     // if both first and last points are conic,
360                     // start at their middle and record its position
361                     // for closure
362                     v_start.x = (v_start.x + v_last.x) / 2;
363                     v_start.y = (v_start.y + v_last.y) / 2;
364 
365                     v_last = v_start;
366                 }
367                 point--;
368                 tags--;
369             }
370 
371             x1 = int26p6_to_dbl(v_start.x);
372             y1 = int26p6_to_dbl(v_start.y);
373             if(flip_y) y1 = -y1;
374             mtx.transform(&x1, &y1);
375             path.move_to(x1,y1);
376 
377             while(point < limit)
378             {
379                 point++;
380                 tags++;
381 
382                 tag = FT_CURVE_TAG(tags[0]);
383                 switch(tag)
384                 {
385                     case FT_CURVE_TAG_ON:  // emit a single line_to
386                     {
387                         x1 = int26p6_to_dbl(point->x);
388                         y1 = int26p6_to_dbl(point->y);
389                         if(flip_y) y1 = -y1;
390                         mtx.transform(&x1, &y1);
391                         path.line_to(x1,y1);
392                         //path.line_to(conv(point->x), flip_y ? -conv(point->y) : conv(point->y));
393                         continue;
394                     }
395 
396                     case FT_CURVE_TAG_CONIC:  // consume conic arcs
397                     {
398                         v_control.x = point->x;
399                         v_control.y = point->y;
400 
401                     Do_Conic:
402                         if(point < limit)
403                         {
404                             FT_Vector vec;
405                             FT_Vector v_middle;
406 
407                             point++;
408                             tags++;
409                             tag = FT_CURVE_TAG(tags[0]);
410 
411                             vec.x = point->x;
412                             vec.y = point->y;
413 
414                             if(tag == FT_CURVE_TAG_ON)
415                             {
416                                 x1 = int26p6_to_dbl(v_control.x);
417                                 y1 = int26p6_to_dbl(v_control.y);
418                                 x2 = int26p6_to_dbl(vec.x);
419                                 y2 = int26p6_to_dbl(vec.y);
420                                 if(flip_y) { y1 = -y1; y2 = -y2; }
421                                 mtx.transform(&x1, &y1);
422                                 mtx.transform(&x2, &y2);
423                                 path.curve3(x1,y1,x2,y2);
424                                 continue;
425                             }
426 
427                             if(tag != FT_CURVE_TAG_CONIC) return false;
428 
429                             v_middle.x = (v_control.x + vec.x) / 2;
430                             v_middle.y = (v_control.y + vec.y) / 2;
431 
432                             x1 = int26p6_to_dbl(v_control.x);
433                             y1 = int26p6_to_dbl(v_control.y);
434                             x2 = int26p6_to_dbl(v_middle.x);
435                             y2 = int26p6_to_dbl(v_middle.y);
436                             if(flip_y) { y1 = -y1; y2 = -y2; }
437                             mtx.transform(&x1, &y1);
438                             mtx.transform(&x2, &y2);
439                             path.curve3(x1,y1,x2,y2);
440 
441                             //path.curve3(conv(v_control.x),
442                             //            flip_y ? -conv(v_control.y) : conv(v_control.y),
443                             //            conv(v_middle.x),
444                             //            flip_y ? -conv(v_middle.y) : conv(v_middle.y));
445 
446                             v_control = vec;
447                             goto Do_Conic;
448                         }
449 
450                         x1 = int26p6_to_dbl(v_control.x);
451                         y1 = int26p6_to_dbl(v_control.y);
452                         x2 = int26p6_to_dbl(v_start.x);
453                         y2 = int26p6_to_dbl(v_start.y);
454                         if(flip_y) { y1 = -y1; y2 = -y2; }
455                         mtx.transform(&x1, &y1);
456                         mtx.transform(&x2, &y2);
457                         path.curve3(x1,y1,x2,y2);
458 
459                         //path.curve3(conv(v_control.x),
460                         //            flip_y ? -conv(v_control.y) : conv(v_control.y),
461                         //            conv(v_start.x),
462                         //            flip_y ? -conv(v_start.y) : conv(v_start.y));
463                         goto Close;
464                     }
465 
466                     default:  // FT_CURVE_TAG_CUBIC
467                     {
468                         FT_Vector vec1, vec2;
469 
470                         if(point + 1 > limit || FT_CURVE_TAG(tags[1]) != FT_CURVE_TAG_CUBIC)
471                         {
472                             return false;
473                         }
474 
475                         vec1.x = point[0].x;
476                         vec1.y = point[0].y;
477                         vec2.x = point[1].x;
478                         vec2.y = point[1].y;
479 
480                         point += 2;
481                         tags  += 2;
482 
483                         if(point <= limit)
484                         {
485                             FT_Vector vec;
486 
487                             vec.x = point->x;
488                             vec.y = point->y;
489 
490                             x1 = int26p6_to_dbl(vec1.x);
491                             y1 = int26p6_to_dbl(vec1.y);
492                             x2 = int26p6_to_dbl(vec2.x);
493                             y2 = int26p6_to_dbl(vec2.y);
494                             x3 = int26p6_to_dbl(vec.x);
495                             y3 = int26p6_to_dbl(vec.y);
496                             if(flip_y) { y1 = -y1; y2 = -y2; y3 = -y3; }
497                             mtx.transform(&x1, &y1);
498                             mtx.transform(&x2, &y2);
499                             mtx.transform(&x3, &y3);
500                             path.curve4(x1,y1,x2,y2,x3,y3);
501 
502                             //path.curve4(conv(vec1.x),
503                             //            flip_y ? -conv(vec1.y) : conv(vec1.y),
504                             //            conv(vec2.x),
505                             //            flip_y ? -conv(vec2.y) : conv(vec2.y),
506                             //            conv(vec.x),
507                             //            flip_y ? -conv(vec.y) : conv(vec.y));
508                             continue;
509                         }
510 
511                         x1 = int26p6_to_dbl(vec1.x);
512                         y1 = int26p6_to_dbl(vec1.y);
513                         x2 = int26p6_to_dbl(vec2.x);
514                         y2 = int26p6_to_dbl(vec2.y);
515                         x3 = int26p6_to_dbl(v_start.x);
516                         y3 = int26p6_to_dbl(v_start.y);
517                         if(flip_y) { y1 = -y1; y2 = -y2; y3 = -y3; }
518                         mtx.transform(&x1, &y1);
519                         mtx.transform(&x2, &y2);
520                         mtx.transform(&x3, &y3);
521                         path.curve4(x1,y1,x2,y2,x3,y3);
522 
523                         //path.curve4(conv(vec1.x),
524                         //            flip_y ? -conv(vec1.y) : conv(vec1.y),
525                         //            conv(vec2.x),
526                         //            flip_y ? -conv(vec2.y) : conv(vec2.y),
527                         //            conv(v_start.x),
528                         //            flip_y ? -conv(v_start.y) : conv(v_start.y));
529                         goto Close;
530                     }
531                 }
532             }
533 
534             path.close_polygon();
535 
536        Close:
537             first = last + 1;
538         }
539 
540         return true;
541     }
542 
agg2RenderPolygonTiled(imageObj * img,shapeObj * p,imageObj * tile)543 int agg2RenderPolygonTiled(imageObj *img, shapeObj *p, imageObj * tile)
544 {
545   assert(img->format->renderer == tile->format->renderer);
546 
547   AGG2Renderer *r = AGG_RENDERER(img);
548   AGG2Renderer *tileRenderer = AGG_RENDERER(tile);
549   polygon_adaptor polygons(p);
550   typedef mapserver::wrap_mode_repeat wrap_type;
551   typedef mapserver::image_accessor_wrap<pixel_format,wrap_type,wrap_type> img_source_type;
552   typedef mapserver::span_pattern_rgba<img_source_type> span_gen_type;
553   mapserver::span_allocator<mapserver::rgba8> sa;
554   r->m_rasterizer_aa.reset();
555   r->m_rasterizer_aa.filling_rule(mapserver::fill_even_odd);
556   img_source_type img_src(tileRenderer->m_pixel_format);
557   span_gen_type sg(img_src, 0, 0);
558   r->m_rasterizer_aa.add_path(polygons);
559   mapserver::render_scanlines_aa(r->m_rasterizer_aa, r->sl_poly, r->m_renderer_base, sa , sg);
560   return MS_SUCCESS;
561 }
562 
agg2RenderGlyphsPath(imageObj * img,textPathObj * tp,colorObj * c,colorObj * oc,int ow,int isMarker)563 int agg2RenderGlyphsPath(imageObj *img, textPathObj *tp, colorObj *c, colorObj *oc, int ow, int isMarker) {
564   mapserver::path_storage glyphs;
565   mapserver::trans_affine trans;
566   AGG2Renderer *r = AGG_RENDERER(img);
567   r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero);
568   for(int i=0; i<tp->numglyphs; i++) {
569     glyphObj *gl  = tp->glyphs + i;
570     trans.reset();
571     trans.rotate(-gl->rot);
572     trans.translate(gl->pnt.x, gl->pnt.y);
573     outline_element *ol = msGetGlyphOutline(gl->face,gl->glyph);
574     if(!ol) {
575       return MS_FAILURE;
576     }
577     decompose_ft_outline(ol->outline,true,trans,glyphs);
578   }
579   mapserver::conv_curve<mapserver::path_storage> m_curves(glyphs);
580   if (oc) {
581     r->m_rasterizer_aa.reset();
582     r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero);
583     mapserver::conv_contour<mapserver::conv_curve<mapserver::path_storage> > cc(m_curves);
584     cc.width(ow + 1);
585     r->m_rasterizer_aa.add_path(cc);
586     r->m_renderer_scanline.color(aggColor(oc));
587     mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_line, r->m_renderer_scanline);
588   }
589   if(c) {
590     r->m_rasterizer_aa.reset();
591     r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero);
592     r->m_rasterizer_aa.add_path(m_curves);
593     r->m_renderer_scanline.color(aggColor(c));
594     mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_line, r->m_renderer_scanline);
595   }
596   return MS_SUCCESS;
597 }
598 
imageVectorSymbol(symbolObj * symbol)599 mapserver::path_storage imageVectorSymbol(symbolObj *symbol)
600 {
601   mapserver::path_storage path;
602   int is_new=1;
603 
604   for(int i=0; i < symbol->numpoints; i++) {
605     if((symbol->points[i].x == -99) && (symbol->points[i].y == -99))
606       is_new=1;
607 
608     else {
609       if(is_new) {
610         path.move_to(symbol->points[i].x,symbol->points[i].y);
611         is_new=0;
612       } else {
613         path.line_to(symbol->points[i].x,symbol->points[i].y);
614       }
615     }
616   }
617   return path;
618 }
619 
agg2RenderVectorSymbol(imageObj * img,double x,double y,symbolObj * symbol,symbolStyleObj * style)620 int agg2RenderVectorSymbol(imageObj *img, double x, double y,
621                            symbolObj *symbol, symbolStyleObj * style)
622 {
623   AGG2Renderer *r = AGG_RENDERER(img);
624   double ox = symbol->sizex * 0.5;
625   double oy = symbol->sizey * 0.5;
626 
627   mapserver::path_storage path = imageVectorSymbol(symbol);
628   mapserver::trans_affine mtx;
629   mtx *= mapserver::trans_affine_translation(-ox,-oy);
630   mtx *= mapserver::trans_affine_scaling(style->scale);
631   mtx *= mapserver::trans_affine_rotation(-style->rotation);
632   mtx *= mapserver::trans_affine_translation(x, y);
633   path.transform(mtx);
634   if (style->color) {
635     r->m_rasterizer_aa.reset();
636     r->m_rasterizer_aa.filling_rule(mapserver::fill_even_odd);
637     r->m_rasterizer_aa.add_path(path);
638     r->m_renderer_scanline.color(aggColor(style->color));
639     mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_poly, r->m_renderer_scanline);
640   }
641   if(style->outlinecolor) {
642     r->m_rasterizer_aa.reset();
643     r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero);
644     r->m_renderer_scanline.color(aggColor(style->outlinecolor));
645     mapserver::conv_stroke<mapserver::path_storage> stroke(path);
646     stroke.width(style->outlinewidth);
647     r->m_rasterizer_aa.add_path(stroke);
648     mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_poly, r->m_renderer_scanline);
649   }
650   return MS_SUCCESS;
651 }
652 
agg2RenderPixmapSymbol(imageObj * img,double x,double y,symbolObj * symbol,symbolStyleObj * style)653 int agg2RenderPixmapSymbol(imageObj *img, double x, double y, symbolObj *symbol, symbolStyleObj * style)
654 {
655   AGG2Renderer *r = AGG_RENDERER(img);
656   rasterBufferObj *pixmap = symbol->pixmap_buffer;
657   assert(pixmap->type == MS_BUFFER_BYTE_RGBA);
658   rendering_buffer b(pixmap->data.rgba.pixels,pixmap->width,pixmap->height,pixmap->data.rgba.row_step);
659   pixel_format pf(b);
660 
661   r->m_rasterizer_aa.reset();
662   r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero);
663   if ( (style->rotation != 0 && style->rotation != MS_PI*2.)|| style->scale != 1) {
664     mapserver::trans_affine image_mtx;
665     image_mtx *= mapserver::trans_affine_translation(-(pf.width()/2.),-(pf.height()/2.));
666     /*agg angles are antitrigonometric*/
667     image_mtx *= mapserver::trans_affine_rotation(-style->rotation);
668     image_mtx *= mapserver::trans_affine_scaling(style->scale);
669 
670 
671 
672     image_mtx *= mapserver::trans_affine_translation(x,y);
673     image_mtx.invert();
674     typedef mapserver::span_interpolator_linear<> interpolator_type;
675     interpolator_type interpolator(image_mtx);
676     mapserver::span_allocator<color_type> sa;
677 
678     // "hardcoded" bilinear filter
679     //------------------------------------------
680     typedef mapserver::span_image_filter_rgba_bilinear_clip<pixel_format, interpolator_type> span_gen_type;
681     span_gen_type sg(pf, mapserver::rgba(0,0,0,0), interpolator);
682     mapserver::path_storage pixmap_bbox;
683     int ims_2 = MS_NINT(MS_MAX(pixmap->width,pixmap->height)*style->scale*1.415)/2+1;
684 
685     pixmap_bbox.move_to(x-ims_2,y-ims_2);
686     pixmap_bbox.line_to(x+ims_2,y-ims_2);
687     pixmap_bbox.line_to(x+ims_2,y+ims_2);
688     pixmap_bbox.line_to(x-ims_2,y+ims_2);
689 
690     r->m_rasterizer_aa.add_path(pixmap_bbox);
691     mapserver::render_scanlines_aa(r->m_rasterizer_aa, r->sl_poly, r->m_renderer_base, sa, sg);
692   } else {
693     //just copy the image at the correct location (we place the pixmap on
694     //the nearest integer pixel to avoid blurring)
695     r->m_renderer_base.blend_from(pf,0,MS_NINT(x-pixmap->width/2.),MS_NINT(y-pixmap->height/2.));
696   }
697   return MS_SUCCESS;
698 }
699 
agg2RenderEllipseSymbol(imageObj * image,double x,double y,symbolObj * symbol,symbolStyleObj * style)700 int agg2RenderEllipseSymbol(imageObj *image, double x, double y,
701                             symbolObj *symbol, symbolStyleObj * style)
702 {
703   AGG2Renderer *r = AGG_RENDERER(image);
704   mapserver::path_storage path;
705   mapserver::ellipse ellipse(x,y,symbol->sizex*style->scale/2,symbol->sizey*style->scale/2);
706   path.concat_path(ellipse);
707   if( style->rotation != 0) {
708     mapserver::trans_affine mtx;
709     mtx *= mapserver::trans_affine_translation(-x,-y);
710     /*agg angles are antitrigonometric*/
711     mtx *= mapserver::trans_affine_rotation(-style->rotation);
712     mtx *= mapserver::trans_affine_translation(x,y);
713     path.transform(mtx);
714   }
715 
716   if(style->color) {
717     r->m_rasterizer_aa.reset();
718     r->m_rasterizer_aa.filling_rule(mapserver::fill_even_odd);
719     r->m_rasterizer_aa.add_path(path);
720     r->m_renderer_scanline.color(aggColor(style->color));
721     mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_line, r->m_renderer_scanline);
722   }
723   if(style->outlinewidth) {
724     r->m_rasterizer_aa.reset();
725     r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero);
726     mapserver::conv_stroke<mapserver::path_storage> stroke(path);
727     stroke.width(style->outlinewidth);
728     r->m_rasterizer_aa.add_path(stroke);
729     r->m_renderer_scanline.color(aggColor(style->outlinecolor));
730     mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_poly, r->m_renderer_scanline);
731   }
732   return MS_SUCCESS;
733 }
734 
agg2RenderTile(imageObj * img,imageObj * tile,double x,double y)735 int agg2RenderTile(imageObj *img, imageObj *tile, double x, double y)
736 {
737   /*
738   AGG2Renderer *imgRenderer = agg2GetRenderer(img);
739   AGG2Renderer *tileRenderer = agg2GetRenderer(tile);
740   */
741   return MS_FAILURE;
742 }
743 
aggInitializeRasterBuffer(rasterBufferObj * rb,int width,int height,int mode)744 int aggInitializeRasterBuffer(rasterBufferObj *rb, int width, int height, int mode)
745 {
746   rb->type = MS_BUFFER_BYTE_RGBA;
747   rb->data.rgba.pixel_step = 4;
748   rb->data.rgba.row_step = rb->data.rgba.pixel_step * width;
749   rb->width = width;
750   rb->height = height;
751   int nBytes = rb->data.rgba.row_step * height;
752   rb->data.rgba.pixels = (band_type*)msSmallCalloc(nBytes,sizeof(band_type));
753   rb->data.rgba.r = &(rb->data.rgba.pixels[band_order::R]);
754   rb->data.rgba.g = &(rb->data.rgba.pixels[band_order::G]);
755   rb->data.rgba.b = &(rb->data.rgba.pixels[band_order::B]);
756   if(mode == MS_IMAGEMODE_RGBA) {
757     rb->data.rgba.a = &(rb->data.rgba.pixels[band_order::A]);
758   }
759   return MS_SUCCESS;
760 }
761 
762 
763 
aggGetRasterBufferHandle(imageObj * img,rasterBufferObj * rb)764 int aggGetRasterBufferHandle(imageObj *img, rasterBufferObj * rb)
765 {
766   AGG2Renderer *r = AGG_RENDERER(img);
767   rb->type =MS_BUFFER_BYTE_RGBA;
768   rb->data.rgba.pixels = r->buffer;
769   rb->data.rgba.row_step = r->m_rendering_buffer.stride();
770   rb->data.rgba.pixel_step = 4;
771   rb->width = r->m_rendering_buffer.width();
772   rb->height = r->m_rendering_buffer.height();
773   rb->data.rgba.r = &(r->buffer[band_order::R]);
774   rb->data.rgba.g = &(r->buffer[band_order::G]);
775   rb->data.rgba.b = &(r->buffer[band_order::B]);
776   if(r->use_alpha)
777     rb->data.rgba.a = &(r->buffer[band_order::A]);
778   else
779     rb->data.rgba.a = NULL;
780   return MS_SUCCESS;
781 }
782 
aggGetRasterBufferCopy(imageObj * img,rasterBufferObj * rb)783 int aggGetRasterBufferCopy(imageObj *img, rasterBufferObj *rb)
784 {
785   AGG2Renderer *r = AGG_RENDERER(img);
786   aggInitializeRasterBuffer(rb, img->width, img->height, MS_IMAGEMODE_RGBA);
787   int nBytes = r->m_rendering_buffer.stride()*r->m_rendering_buffer.height();
788   memcpy(rb->data.rgba.pixels,r->buffer, nBytes);
789   return MS_SUCCESS;
790 }
791 
792 
793 
agg2MergeRasterBuffer(imageObj * dest,rasterBufferObj * overlay,double opacity,int srcX,int srcY,int dstX,int dstY,int width,int height)794 int agg2MergeRasterBuffer(imageObj *dest, rasterBufferObj *overlay, double opacity, int srcX, int srcY,
795                           int dstX, int dstY, int width, int height)
796 {
797   assert(overlay->type == MS_BUFFER_BYTE_RGBA);
798   rendering_buffer b(overlay->data.rgba.pixels, overlay->width, overlay->height, overlay->data.rgba.row_step);
799   pixel_format pf(b);
800   AGG2Renderer *r = AGG_RENDERER(dest);
801   mapserver::rect_base<int> src_rect(srcX,srcY,srcX+width,srcY+height);
802   r->m_renderer_base.blend_from(pf,&src_rect, dstX-srcX, dstY-srcY, unsigned(opacity * 255));
803   return MS_SUCCESS;
804 }
805 
806 /* image i/o */
agg2CreateImage(int width,int height,outputFormatObj * format,colorObj * bg)807 imageObj *agg2CreateImage(int width, int height, outputFormatObj *format, colorObj * bg)
808 {
809   imageObj *image = NULL;
810   if (format->imagemode != MS_IMAGEMODE_RGB && format->imagemode != MS_IMAGEMODE_RGBA) {
811     msSetError(MS_MISCERR,
812                "AGG2 driver only supports RGB or RGBA pixel models.", "agg2CreateImage()");
813     return image;
814   }
815   image = (imageObj *) calloc(1, sizeof (imageObj));
816   MS_CHECK_ALLOC(image, sizeof (imageObj), NULL);
817   AGG2Renderer *r = new AGG2Renderer();
818 
819   /* Compute size on 64bit and check that it is compatible of the platform size_t */
820   AGG_INT64U bufSize64 = (AGG_INT64U)width * height * 4 * sizeof(band_type);
821   size_t bufSize = (size_t)bufSize64;
822   if( (AGG_INT64U)bufSize != bufSize64 ) {
823     msSetError(MS_MEMERR, "%s: %d: Out of memory allocating " AGG_INT64U_FRMT " bytes.\n", "agg2CreateImage()",
824                __FILE__, __LINE__, bufSize64);
825     free(image);
826     delete r;
827     return NULL;
828   }
829 
830   r->buffer = (band_type*)malloc(bufSize);
831   if (r->buffer == NULL) {
832     msSetError(MS_MEMERR, "%s: %d: Out of memory allocating " AGG_INT64U_FRMT " bytes.\n", "agg2CreateImage()",
833                __FILE__, __LINE__, bufSize64);
834     free(image);
835     delete r;
836     return NULL;
837   }
838   r->m_rendering_buffer.attach(r->buffer, width, height, width * 4);
839   r->m_pixel_format.attach(r->m_rendering_buffer);
840   r->m_compop_pixel_format.attach(r->m_rendering_buffer);
841   r->m_renderer_base.attach(r->m_pixel_format);
842   r->m_compop_renderer_base.attach(r->m_compop_pixel_format);
843   r->m_renderer_scanline.attach(r->m_renderer_base);
844   r->default_gamma = atof(msGetOutputFormatOption( format, "GAMMA", "0.75" ));
845   if(r->default_gamma <= 0.0 || r->default_gamma >= 1.0) {
846     r->default_gamma = 0.75;
847   }
848   r->gamma_function.set(0,r->default_gamma);
849   r->m_rasterizer_aa_gamma.gamma(r->gamma_function);
850   if( bg && !format->transparent )
851     r->m_renderer_base.clear(aggColor(bg));
852   else
853     r->m_renderer_base.clear(AGG_NO_COLOR);
854 
855   if (!bg || format->transparent || format->imagemode == MS_IMAGEMODE_RGBA ) {
856     r->use_alpha = true;
857   } else {
858     r->use_alpha = false;
859   }
860   image->img.plugin = (void*) r;
861 
862   return image;
863 }
864 
agg2SaveImage(imageObj * img,mapObj * map,FILE * fp,outputFormatObj * format)865 int agg2SaveImage(imageObj *img, mapObj* map, FILE *fp, outputFormatObj * format)
866 {
867 
868 
869   return MS_FAILURE;
870 }
871 
agg2StartNewLayer(imageObj * img,mapObj * map,layerObj * layer)872 int agg2StartNewLayer(imageObj *img, mapObj*map, layerObj *layer)
873 {
874   AGG2Renderer *r = AGG_RENDERER(img);
875   char *sgamma = msLayerGetProcessingKey( layer, "GAMMA" );
876   double gamma;
877   if(sgamma) {
878     gamma = atof(sgamma);
879     if(gamma <= 0 || gamma >= 1) gamma = 0.75;
880   } else {
881     gamma = r->default_gamma;
882   }
883   if(r->gamma_function.end() != gamma) {
884     r->gamma_function.end(gamma);
885     r->m_rasterizer_aa_gamma.gamma(r->gamma_function);
886   }
887   return MS_SUCCESS;
888 }
889 
agg2CloseNewLayer(imageObj * img,mapObj * map,layerObj * layer)890 int agg2CloseNewLayer(imageObj *img, mapObj *map, layerObj *layer)
891 {
892   return MS_SUCCESS;
893 }
894 
agg2FreeImage(imageObj * image)895 int agg2FreeImage(imageObj * image)
896 {
897   AGG2Renderer *r = AGG_RENDERER(image);
898   free(r->buffer);
899   delete r;
900   image->img.plugin = NULL;
901   return MS_SUCCESS;
902 }
903 
agg2FreeSymbol(symbolObj * symbol)904 int agg2FreeSymbol(symbolObj * symbol)
905 {
906   return MS_SUCCESS;
907 }
908 
agg2InitCache(void ** vcache)909 int agg2InitCache(void **vcache)
910 {
911   aggRendererCache *cache = new aggRendererCache();
912   *vcache = (void*)cache;
913   return MS_SUCCESS;
914 }
915 
agg2Cleanup(void * vcache)916 int agg2Cleanup(void *vcache)
917 {
918   aggRendererCache *cache = (aggRendererCache*)vcache;
919   delete cache;
920   return MS_SUCCESS;
921 }
922 
923 
924 // ------------------------------------------------------------------------
925 // Function to create a custom hatch symbol based on an arbitrary angle.
926 // ------------------------------------------------------------------------
createHatch(double ox,double oy,double rx,double ry,int sx,int sy,double angle,double step)927 static mapserver::path_storage createHatch(double ox, double oy,
928     double rx, double ry,
929     int sx, int sy, double angle, double step)
930 {
931   mapserver::path_storage path;
932   //restrict the angle to [0 180[, i.e ]-pi/2,pi/2] in radians
933   angle = fmod(angle, 360.0);
934   if(angle < 0) angle += 360;
935   if(angle >= 180) angle -= 180;
936 
937   //treat 2 easy cases which would cause divide by 0 in generic case
938   if(angle==0) {
939     double y0 = step-fmod(oy-ry,step);
940     if((oy - ry) < 0) {
941       y0 -= step;
942     }
943     for(double y=y0; y<sy; y+=step) {
944       path.move_to(0,y);
945       path.line_to(sx,y);
946     }
947     return path;
948   }
949   if(angle==90) {
950     double x0 = step-fmod(ox-rx,step);
951     if((ox - rx) < 0) {
952       x0 -= step;
953     }
954     for(double x=x0; x<sx; x+=step) {
955       path.move_to(x,0);
956       path.line_to(x,sy);
957     }
958     return path;
959   }
960 
961 
962   double theta = (90-angle)*MS_DEG_TO_RAD; /* theta in ]-pi/2 , pi/2] */
963   double ct = cos(theta);
964   double st = sin(theta);
965   double invct = 1.0/ct;
966   double invst = 1.0/st;
967   double r0; /* distance from first hatch line to the top-left (if angle in  0,pi/2)
968                   or bottom-left (if angle in -pi/2,0) corner of the hatch bbox */
969   double rmax = sqrt((double)(sx*sx+sy*sy)); /* distance to the furthest hatch we will have to create
970 TODO: this could be optimized for bounding boxes where width is very different than height for
971 certain hatch angles */
972   double rref= rx*ct + ry*st; /* distance to the line passing through the refpoint, origin is
973                                    (0,0) of the imageObj */
974   double rcorner; /* distance to the line passing through the topleft or bottomleft corner
975                        of the hatch bbox (origin is (0,0) of imageObj) */
976 
977   /* calculate the distance from the refpoint to the top right of the path */
978   if(angle < 90) {
979     rcorner = ox*ct + oy*st;
980     r0 = step - fmod(rcorner-rref,step);
981     if(rcorner-rref<0) r0 -= step;
982   } else {
983     rcorner = ox*ct + (oy+sy)*st;
984     r0 = step - fmod(rcorner-rref,step);
985     if(rcorner-rref<0) r0 -= step;
986     st = -st;
987     invst = -invst;
988   }
989 
990 
991   //parametrize each line as r = x.cos(theta) + y.sin(theta)
992   for(double r=r0; r<rmax; r+=step) {
993     int inter=0;
994     double x,y;
995     double pt[4]; //array to store the coordinates of intersection of the line with the sides
996     //in the general case there will only be two intersections
997     //so pt[4] should be sufficient to store the coordinates of the intersection,
998     //but we allocate pt[8] to treat the special and rare/unfortunate case when the
999     //line is a perfect diagonal (and therfore intersects all four sides)
1000     //note that the order for testing is important in this case so that the first
1001     //two intersection points actually correspond to the diagonal and not a degenerate line
1002 
1003     //test for intersection with each side
1004 
1005     y=r*invst;
1006     x=0; // test for intersection with left of image
1007     if(y>=0&&y<=sy) {
1008       pt[2*inter]=x;
1009       pt[2*inter+1]=y;
1010       inter++;
1011     }
1012     x=sx;
1013     y=(r-sx*ct)*invst;// test for intersection with right of image
1014     if(y>=0&&y<=sy) {
1015       pt[2*inter]=x;
1016       pt[2*inter+1]=y;
1017       inter++;
1018     }
1019     if(inter<2) {
1020       y=0;
1021       x=r*invct;// test for intersection with top of image
1022       if(x>=0&&x<=sx) {
1023         pt[2*inter]=x;
1024         pt[2*inter+1]=y;
1025         inter++;
1026       }
1027     }
1028     if(inter<2) {
1029       y=sy;
1030       x=(r-sy*st)*invct;// test for intersection with bottom of image
1031       if(x>=0&&x<=sx) {
1032         pt[2*inter]=x;
1033         pt[2*inter+1]=y;
1034         inter++;
1035       }
1036     }
1037     if(inter==2 && (pt[0]!=pt[2] || pt[1]!=pt[3])) {
1038       //the line intersects with two sides of the image, it should therefore be drawn
1039       if(angle<90) {
1040         path.move_to(pt[0],pt[1]);
1041         path.line_to(pt[2],pt[3]);
1042       } else {
1043         path.move_to(pt[0],sy-pt[1]);
1044         path.line_to(pt[2],sy-pt[3]);
1045       }
1046     }
1047   }
1048   return path;
1049 }
1050 
renderPolygonHatches(imageObj * img,VertexSource & clipper,colorObj * color)1051 template<class VertexSource> int renderPolygonHatches(imageObj *img,VertexSource &clipper, colorObj *color)
1052 {
1053   if(img->format->renderer == MS_RENDER_WITH_AGG) {
1054     AGG2Renderer *r = AGG_RENDERER(img);
1055     r->m_rasterizer_aa_gamma.reset();
1056     r->m_rasterizer_aa_gamma.filling_rule(mapserver::fill_non_zero);
1057     r->m_rasterizer_aa_gamma.add_path(clipper);
1058     r->m_renderer_scanline.color(aggColor(color));
1059     mapserver::render_scanlines(r->m_rasterizer_aa_gamma, r->sl_poly, r->m_renderer_scanline);
1060   } else {
1061     shapeObj shape;
1062     msInitShape(&shape);
1063     int allocated = 20;
1064     lineObj line;
1065     shape.line = &line;
1066     shape.numlines = 1;
1067     shape.line[0].point = (pointObj*)msSmallCalloc(allocated,sizeof(pointObj));
1068     shape.line[0].numpoints = 0;
1069     double x=0,y=0;
1070     unsigned int cmd;
1071     clipper.rewind(0);
1072     while((cmd = clipper.vertex(&x,&y)) != mapserver::path_cmd_stop) {
1073       switch(cmd) {
1074         case mapserver::path_cmd_line_to:
1075           if(shape.line[0].numpoints == allocated) {
1076             allocated *= 2;
1077             shape.line[0].point = (pointObj*)msSmallRealloc(shape.line[0].point, allocated*sizeof(pointObj));
1078           }
1079           shape.line[0].point[shape.line[0].numpoints].x = x;
1080           shape.line[0].point[shape.line[0].numpoints].y = y;
1081           shape.line[0].numpoints++;
1082           break;
1083         case mapserver::path_cmd_move_to:
1084           shape.line[0].point[0].x = x;
1085           shape.line[0].point[0].y = y;
1086           shape.line[0].numpoints = 1;
1087           break;
1088         case mapserver::path_cmd_end_poly|mapserver::path_flags_close:
1089           if(shape.line[0].numpoints > 2) {
1090             if(UNLIKELY(MS_FAILURE == MS_IMAGE_RENDERER(img)->renderPolygon(img,&shape,color))) {
1091               free(shape.line[0].point);
1092               return MS_FAILURE;
1093             }
1094           }
1095           break;
1096         default:
1097           assert(0); //WTF?
1098       }
1099     }
1100     free(shape.line[0].point);
1101   }
1102   return MS_SUCCESS;
1103 }
1104 
msHatchPolygon(imageObj * img,shapeObj * poly,double spacing,double width,double * pattern,int patternlength,double angle,colorObj * color)1105 int msHatchPolygon(imageObj *img, shapeObj *poly, double spacing, double width, double *pattern, int patternlength, double angle, colorObj *color)
1106 {
1107   assert(MS_RENDERER_PLUGIN(img->format));
1108   msComputeBounds(poly);
1109 
1110   /* amount we should expand the bounding box by */
1111   double exp = width * 0.7072;
1112 
1113   /* width and height of the bounding box we will be creating the hatch in */
1114   int pw=(int)(poly->bounds.maxx-poly->bounds.minx+exp*2)+1;
1115   int ph=(int)(poly->bounds.maxy-poly->bounds.miny+exp*2)+1;
1116 
1117   /* position of the top-left corner of the bounding box */
1118   double ox = poly->bounds.minx - exp;
1119   double oy = poly->bounds.miny - exp;
1120 
1121   //create a rectangular hatch of size pw,ph starting at 0,0
1122   //the created hatch is of the size of the shape's bounding box
1123   mapserver::path_storage hatch = createHatch(ox,oy,
1124                                   img->refpt.x,img->refpt.y,pw,ph,angle,spacing);
1125   if(hatch.total_vertices()<=0) return MS_SUCCESS;
1126 
1127   //translate the hatch so it overlaps the current shape
1128   hatch.transform(mapserver::trans_affine_translation(ox,oy));
1129 
1130   polygon_adaptor polygons(poly);
1131 
1132 
1133 
1134   if(patternlength>1) {
1135     //dash the color-hatch and render it clipped by the shape
1136     mapserver::conv_dash<mapserver::path_storage > dash(hatch);
1137     mapserver::conv_stroke<mapserver::conv_dash<mapserver::path_storage> > stroke(dash);
1138     for (int i=0; i<patternlength; i+=2) {
1139       if (i < patternlength-1) {
1140         dash.add_dash(pattern[i], pattern[i+1]);
1141       }
1142     }
1143     stroke.width(width);
1144     stroke.line_cap(mapserver::butt_cap);
1145     mapserver::conv_clipper<polygon_adaptor,mapserver::conv_stroke<mapserver::conv_dash<mapserver::path_storage> > > clipper(polygons,stroke, mapserver::clipper_and);
1146     renderPolygonHatches(img,clipper,color);
1147   } else {
1148     //render the hatch clipped by the shape
1149     mapserver::conv_stroke <mapserver::path_storage > stroke(hatch);
1150     stroke.width(width);
1151     stroke.line_cap(mapserver::butt_cap);
1152     mapserver::conv_clipper<polygon_adaptor,mapserver::conv_stroke<mapserver::path_storage> > clipper(polygons,stroke, mapserver::clipper_and);
1153     renderPolygonHatches(img,clipper,color);
1154   }
1155 
1156 
1157   //assert(prevCmd == mapserver::path_cmd_line_to);
1158   //delete lines;
1159   return MS_SUCCESS;
1160 }
1161 
1162 #ifdef USE_PIXMAN
ms2pixman_compop(CompositingOperation c)1163 static pixman_op_t ms2pixman_compop(CompositingOperation c) {
1164   switch(c) {
1165     case MS_COMPOP_CLEAR:
1166       return PIXMAN_OP_CLEAR;
1167     case MS_COMPOP_SRC:
1168       return PIXMAN_OP_SRC;
1169     case MS_COMPOP_DST:
1170       return PIXMAN_OP_DST;
1171     case MS_COMPOP_SRC_OVER:
1172       return PIXMAN_OP_OVER;
1173     case MS_COMPOP_DST_OVER:
1174       return PIXMAN_OP_OVER_REVERSE;
1175     case MS_COMPOP_SRC_IN:
1176       return PIXMAN_OP_IN;
1177     case MS_COMPOP_DST_IN:
1178       return PIXMAN_OP_IN_REVERSE;
1179     case MS_COMPOP_SRC_OUT:
1180       return PIXMAN_OP_OUT;
1181     case MS_COMPOP_DST_OUT:
1182       return PIXMAN_OP_OUT_REVERSE;
1183     case MS_COMPOP_SRC_ATOP:
1184       return PIXMAN_OP_ATOP;
1185     case MS_COMPOP_DST_ATOP:
1186       return PIXMAN_OP_ATOP_REVERSE;
1187     case MS_COMPOP_XOR:
1188       return PIXMAN_OP_XOR;
1189     case MS_COMPOP_PLUS:
1190       return PIXMAN_OP_ADD;
1191     case MS_COMPOP_MULTIPLY:
1192       return PIXMAN_OP_MULTIPLY;
1193     case MS_COMPOP_SCREEN:
1194       return PIXMAN_OP_SCREEN;
1195     case MS_COMPOP_OVERLAY:
1196       return PIXMAN_OP_OVERLAY;
1197     case MS_COMPOP_DARKEN:
1198       return PIXMAN_OP_DARKEN;
1199     case MS_COMPOP_LIGHTEN:
1200       return PIXMAN_OP_LIGHTEN;
1201     case MS_COMPOP_COLOR_DODGE:
1202       return PIXMAN_OP_COLOR_DODGE;
1203     case MS_COMPOP_COLOR_BURN:
1204       return PIXMAN_OP_COLOR_DODGE;
1205     case MS_COMPOP_HARD_LIGHT:
1206       return PIXMAN_OP_HARD_LIGHT;
1207     case MS_COMPOP_SOFT_LIGHT:
1208       return PIXMAN_OP_SOFT_LIGHT;
1209     case MS_COMPOP_DIFFERENCE:
1210       return PIXMAN_OP_DIFFERENCE;
1211     case MS_COMPOP_EXCLUSION:
1212       return PIXMAN_OP_EXCLUSION;
1213     case MS_COMPOP_INVERT:
1214     case MS_COMPOP_INVERT_RGB:
1215     case MS_COMPOP_MINUS:
1216     case MS_COMPOP_CONTRAST:
1217     default:
1218       return PIXMAN_OP_OVER;
1219   }
1220 }
1221 #else
ms2agg_compop(CompositingOperation c)1222 static mapserver::comp_op_e ms2agg_compop(CompositingOperation c) {
1223   switch(c) {
1224     case MS_COMPOP_CLEAR:
1225       return mapserver::comp_op_clear;
1226     case MS_COMPOP_SRC:
1227       return mapserver::comp_op_src;
1228     case MS_COMPOP_DST:
1229       return mapserver::comp_op_dst;
1230     case MS_COMPOP_SRC_OVER:
1231       return mapserver::comp_op_src_over;
1232     case MS_COMPOP_DST_OVER:
1233       return mapserver::comp_op_dst_over;
1234     case MS_COMPOP_SRC_IN:
1235       return mapserver::comp_op_src_in;
1236     case MS_COMPOP_DST_IN:
1237       return mapserver::comp_op_dst_in;
1238     case MS_COMPOP_SRC_OUT:
1239       return mapserver::comp_op_src_out;
1240     case MS_COMPOP_DST_OUT:
1241       return mapserver::comp_op_dst_out;
1242     case MS_COMPOP_SRC_ATOP:
1243       return mapserver::comp_op_src_atop;
1244     case MS_COMPOP_DST_ATOP:
1245       return mapserver::comp_op_dst_atop;
1246     case MS_COMPOP_XOR:
1247       return mapserver::comp_op_xor;
1248     case MS_COMPOP_PLUS:
1249       return mapserver::comp_op_plus;
1250     case MS_COMPOP_MINUS:
1251       return mapserver::comp_op_minus;
1252     case MS_COMPOP_MULTIPLY:
1253       return mapserver::comp_op_multiply;
1254     case MS_COMPOP_SCREEN:
1255       return mapserver::comp_op_screen;
1256     case MS_COMPOP_OVERLAY:
1257       return mapserver::comp_op_overlay;
1258     case MS_COMPOP_DARKEN:
1259       return mapserver::comp_op_darken;
1260     case MS_COMPOP_LIGHTEN:
1261       return mapserver::comp_op_lighten;
1262     case MS_COMPOP_COLOR_DODGE:
1263       return mapserver::comp_op_color_dodge;
1264     case MS_COMPOP_COLOR_BURN:
1265       return mapserver::comp_op_color_burn;
1266     case MS_COMPOP_HARD_LIGHT:
1267       return mapserver::comp_op_hard_light;
1268     case MS_COMPOP_SOFT_LIGHT:
1269       return mapserver::comp_op_soft_light;
1270     case MS_COMPOP_DIFFERENCE:
1271       return mapserver::comp_op_difference;
1272     case MS_COMPOP_EXCLUSION:
1273       return mapserver::comp_op_exclusion;
1274     case MS_COMPOP_CONTRAST:
1275       return mapserver::comp_op_contrast;
1276     case MS_COMPOP_INVERT:
1277       return mapserver::comp_op_invert;
1278     case MS_COMPOP_INVERT_RGB:
1279       return mapserver::comp_op_invert_rgb;
1280     default:
1281       return mapserver::comp_op_src_over;
1282   }
1283 }
1284 #endif
1285 
aggCompositeRasterBuffer(imageObj * dest,rasterBufferObj * overlay,CompositingOperation comp,int opacity)1286 int aggCompositeRasterBuffer(imageObj *dest, rasterBufferObj *overlay, CompositingOperation comp, int opacity) {
1287   assert(overlay->type == MS_BUFFER_BYTE_RGBA);
1288   AGG2Renderer *r = AGG_RENDERER(dest);
1289 #ifdef USE_PIXMAN
1290   pixman_image_t *si = pixman_image_create_bits(PIXMAN_a8r8g8b8,overlay->width,overlay->height,
1291                        (uint32_t*)overlay->data.rgba.pixels,overlay->data.rgba.row_step);
1292   pixman_image_t *bi = pixman_image_create_bits(PIXMAN_a8r8g8b8,dest->width,dest->height,
1293                        (uint32_t*)r->buffer,dest->width*4);
1294   pixman_image_t *alpha_mask_i=NULL, *alpha_mask_i_ptr;
1295   pixman_image_set_filter(si,PIXMAN_FILTER_NEAREST, NULL, 0);
1296   unsigned char *alpha_mask = NULL;
1297     if(opacity > 0) {
1298       if(opacity == 100) {
1299         alpha_mask_i_ptr = NULL;
1300       } else {
1301           unsigned char alpha = (unsigned char)(opacity * 2.55);
1302           if(!alpha_mask_i) {
1303               alpha_mask = (unsigned char*)msSmallMalloc(dest->width * dest->height);
1304               alpha_mask_i = pixman_image_create_bits(PIXMAN_a8,dest->width,dest->height,
1305                       (uint32_t*)alpha_mask,dest->width);
1306           }
1307           memset(alpha_mask,alpha,dest->width*dest->height);
1308           alpha_mask_i_ptr = alpha_mask_i;
1309       }
1310       pixman_image_composite (ms2pixman_compop(comp), si, alpha_mask_i_ptr, bi,
1311                   0, 0, 0, 0, 0, 0, dest->width,dest->height);
1312     }
1313   pixman_image_unref(si);
1314   pixman_image_unref(bi);
1315   if(alpha_mask_i) {
1316     pixman_image_unref(alpha_mask_i);
1317     msFree(alpha_mask);
1318   }
1319   return MS_SUCCESS;
1320 #else
1321   rendering_buffer b(overlay->data.rgba.pixels, overlay->width, overlay->height, overlay->data.rgba.row_step);
1322   pixel_format pf(b);
1323   mapserver::comp_op_e comp_op = ms2agg_compop(comp);
1324   if(comp_op == mapserver::comp_op_src_over) {
1325     r->m_renderer_base.blend_from(pf,0,0,0,unsigned(opacity * 2.55));
1326   } else {
1327     compop_pixel_format pixf(r->m_rendering_buffer);
1328     compop_renderer_base ren(pixf);
1329     pixf.comp_op(comp_op);
1330     ren.blend_from(pf,0,0,0,unsigned(opacity * 2.55));
1331   }
1332   return MS_SUCCESS;
1333 #endif
1334 }
1335 
msApplyBlurringCompositingFilter(rasterBufferObj * rb,unsigned int radius)1336 void msApplyBlurringCompositingFilter(rasterBufferObj *rb, unsigned int radius) {
1337   rendering_buffer b(rb->data.rgba.pixels, rb->width, rb->height, rb->data.rgba.row_step);
1338   pixel_format pf(b);
1339   mapserver::stack_blur_rgba32(pf,radius,radius);
1340 }
1341 
msPopulateRendererVTableAGG(rendererVTableObj * renderer)1342 int msPopulateRendererVTableAGG(rendererVTableObj * renderer)
1343 {
1344   renderer->compositeRasterBuffer = &aggCompositeRasterBuffer;
1345   renderer->supports_pixel_buffer = 1;
1346   renderer->use_imagecache = 0;
1347   renderer->supports_clipping = 0;
1348   renderer->supports_svg = 0;
1349   renderer->default_transform_mode = MS_TRANSFORM_SIMPLIFY;
1350   agg2InitCache(&(MS_RENDERER_CACHE(renderer)));
1351   renderer->cleanup = agg2Cleanup;
1352   renderer->renderLine = &agg2RenderLine;
1353 
1354   renderer->renderPolygon = &agg2RenderPolygon;
1355   renderer->renderPolygonTiled = &agg2RenderPolygonTiled;
1356   renderer->renderLineTiled = &agg2RenderLineTiled;
1357 
1358   renderer->renderGlyphs = &agg2RenderGlyphsPath;
1359 
1360   renderer->renderVectorSymbol = &agg2RenderVectorSymbol;
1361 
1362   renderer->renderPixmapSymbol = &agg2RenderPixmapSymbol;
1363 
1364   renderer->renderEllipseSymbol = &agg2RenderEllipseSymbol;
1365 
1366   renderer->renderTile = &agg2RenderTile;
1367 
1368   renderer->getRasterBufferHandle = &aggGetRasterBufferHandle;
1369   renderer->getRasterBufferCopy = aggGetRasterBufferCopy;
1370   renderer->initializeRasterBuffer = aggInitializeRasterBuffer;
1371 
1372   renderer->mergeRasterBuffer = &agg2MergeRasterBuffer;
1373   renderer->loadImageFromFile = msLoadMSRasterBufferFromFile;
1374   renderer->createImage = &agg2CreateImage;
1375   renderer->saveImage = &agg2SaveImage;
1376 
1377   renderer->startLayer = &agg2StartNewLayer;
1378   renderer->endLayer = &agg2CloseNewLayer;
1379 
1380   renderer->freeImage = &agg2FreeImage;
1381   renderer->freeSymbol = &agg2FreeSymbol;
1382   renderer->cleanup = agg2Cleanup;
1383 
1384   return MS_SUCCESS;
1385 }
1386