root/branches/branch-5-0/mapserver/mapagg.cpp

Revision 7526, 99.5 kB (checked in by tbonfort, 7 months ago)

Fix rendering of non filled vector symbols (#2585)

  • Property svn:keywords set to Author Date Id Revision
Line 
1 /******************************************************************************
2  *
3  * Project:  MapServer
4  * Purpose:  AGG rendering and other AGG related functions.
5  * Author:   Steve Lime and the MapServer team.
6  *
7  ******************************************************************************
8  * Copyright (c) 1996-2007 Regents of the University of Minnesota.
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included in
18  * all copies of this Software or works derived from this Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  *****************************************************************************/
28
29 #ifdef USE_AGG
30
31 #include "mapserver.h"
32 #include "mapthread.h"
33
34
35 #ifdef _WIN32
36 #include <fcntl.h>
37 #include <io.h>
38 #endif
39
40 #include "agg_basics.h"
41 #include "agg_rendering_buffer.h"
42 #include "agg_rasterizer_scanline_aa.h"
43 #include "agg_rasterizer_outline_aa.h"
44 #include "agg_rasterizer_outline.h"
45
46 #include "agg_conv_stroke.h"
47 #include "agg_conv_dash.h"
48 #include "agg_conv_curve.h"
49 #include "agg_conv_contour.h"
50
51 #include "agg_scanline_p.h"
52 #include "agg_path_storage.h"
53
54 #include "agg_renderer_base.h"
55 #include "agg_renderer_scanline.h"
56 #include "agg_renderer_primitives.h"
57 #include "agg_renderer_outline_aa.h"
58 #include "agg_renderer_outline_image.h"
59
60 #include "agg_pixfmt_rgb.h"
61 #include "agg_pixfmt_rgba.h"
62
63 #include "agg_arc.h"
64 #include "agg_ellipse.h"
65
66 #include "agg_pattern_filters_rgba.h"
67
68 #include "agg_bounding_rect.h"
69 #include "agg_span_allocator.h"
70 #include "agg_image_accessors.h"
71 #include "agg_span_pattern_rgba.h"
72 #include "agg_span_interpolator_linear.h"
73 #include "agg_span_image_filter_rgba.h"
74 #include "agg_span_image_filter_rgb.h"
75
76 #include "agg_trans_affine.h"
77 #include "agg_scanline_boolean_algebra.h"
78 #include "agg_scanline_storage_aa.h"
79 #include "math.h"
80 #include "mapagg.h"
81
82 #include <ft2build.h>
83 #include "agg_font_freetype.h"
84 #include "agg_font_cache_manager.h"
85 #define LINESPACE 1.33 //space beween text lines... from GD
86
87 #ifdef CPL_MSB
88 typedef agg::pixfmt_argb32 GDpixfmt;
89 typedef agg::pixfmt_alpha_blend_rgba<agg::blender_argb32_plain,mapserv_row_ptr_cache<int>,int> pixelFormat;
90 typedef agg::pixfmt_alpha_blend_rgba<agg::blender_argb32_pre,mapserv_row_ptr_cache<int>,int> pixelFormat_pre;
91 #else
92 typedef agg::pixfmt_bgra32 GDpixfmt;
93 typedef agg::pixfmt_alpha_blend_rgba<agg::blender_bgra32_plain,mapserv_row_ptr_cache<int>,int> pixelFormat;
94 typedef agg::pixfmt_alpha_blend_rgba<agg::blender_bgra32_pre,mapserv_row_ptr_cache<int>,int> pixelFormat_pre;
95
96 //this how to render to a gdImg while ignoring its funky alpha channel
97 //however this doesn't work with RGBA imagemode. un-ifdef the endesction in mapagg.h to use
98 //typedef agg::pixfmt_alpha_blend_rgb_gd<ms_blender_bgr24,mapserv_row_ptr_cache<int> > pixelFormat;
99 //typedef agg::pixfmt_alpha_blend_rgb_gd<ms_blender_bgr24_pre,mapserv_row_ptr_cache<int> > pixelFormat_pre;
100
101 #endif
102
103
104
105 typedef agg::rgba8 color_type;
106 typedef agg::font_engine_freetype_int16 font_engine_type;
107 typedef agg::font_cache_manager<font_engine_type> font_manager_type;
108 typedef agg::conv_curve<font_manager_type::path_adaptor_type> font_curve_type;
109
110 typedef agg::renderer_base<pixelFormat> renderer_base;
111 typedef agg::renderer_base<pixelFormat_pre> renderer_base_pre;
112
113 typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_aa;
114 typedef agg::renderer_outline_aa<renderer_base> renderer_oaa;
115 typedef agg::renderer_primitives<renderer_base> renderer_prim;
116 typedef agg::rasterizer_outline_aa<renderer_oaa> rasterizer_outline_aa;
117 typedef agg::rasterizer_outline <renderer_prim> rasterizer_outline;
118 typedef agg::rasterizer_scanline_aa<> rasterizer_scanline;
119 typedef agg::scanline_p8 scanline;
120
121 MS_CVSID("$Id$")
122
123 ///transform a mapserver shapeobj to an agg path_storage
124 ///@param close set to true for polygons. set to false for lines
125 ///@param ox,oy offset the shape by the given number of pixels
126 ///@returns an agg path_storage
127 static agg::path_storage shapeToPath(shapeObj *p, bool close, double ox=0, double oy=0) {
128     agg::path_storage path;
129     for(int i = 0; i < p->numlines; i++) {
130         path.move_to(p->line[i].point[0].x, p->line[i].point[0].y);
131         for(int j=1; j<p->line[i].numpoints; j++)
132             path.line_to(p->line[i].point[j].x, p->line[i].point[j].y);
133         if(close)
134             path.close_polygon();
135     }
136     if(ox!=0||oy!=0) {
137         path.transform(agg::trans_affine_translation(ox,oy));
138     }
139     return path;
140 }
141
142 ///shortcut function to create a path_storage representing a polygon
143 ///from a shapeobj
144 static agg::path_storage shapePolygonToPath(shapeObj *p, double ox=0, double oy=0) {
145     return shapeToPath(p,true,ox,oy);
146 }
147
148 ///shortcut function to create a path_storage representing a polyline
149 ///from a shapeobj
150 static agg::path_storage shapePolylineToPath(shapeObj *p, double ox=0, double oy=0) {
151     return shapeToPath(p,false,ox,oy);
152 }
153
154 ///apply line styling functions. applies the line joining and capping
155 ///parameters from the symbolobj to the given stroke
156 ///@param stroke the stroke to which we apply the styling
157 ///@param symbol the symbolobj which may contain the styling info
158 template<class VertexSource>
159 static void strokeFromSymbol(VertexSource &stroke, symbolObj *symbol) {
160     switch(symbol->linejoin) {
161     case MS_CJC_ROUND:
162         stroke.line_join(agg::round_join);
163         break;
164     case MS_CJC_MITER:
165         stroke.line_join(agg::miter_join);
166         break;
167     case MS_CJC_BEVEL:
168         stroke.line_join(agg::bevel_join);
169         break;
170     }
171     switch(symbol->linecap) {
172     case MS_CJC_BUTT:
173         stroke.line_cap(agg::butt_cap);
174         break;
175     case MS_CJC_ROUND:
176         stroke.line_cap(agg::round_cap);
177         break;
178     case MS_CJC_SQUARE:
179         stroke.line_cap(agg::square_cap);
180         break;
181     }
182 }
183 ///
184 ///returns a rendering buffer containing the argb pixel values of the gd image.
185 ///the function expects an image with a "gd style" alpha channel, i.e.
186 ///127...0 actually mapping to 0...255
187 ///NOTE: it is your responsability to free the rendering buffer's memory
188 ///with a call to delete[](rendering_buffer.buf())
189 ///
190
191 static agg::rendering_buffer gdImg2AGGRB_BGRA(gdImagePtr img) {
192     int width=img->sx;
193     int height=img->sy;
194     agg::int8u*           im_data;
195     agg::rendering_buffer im_data_rbuf;
196     im_data = new agg::int8u[width * height * 4];
197     im_data_rbuf.attach(im_data, width,height, width*4);
198     for(int row=0;row<height;row++) {
199         unsigned int* rowptr=(unsigned int*)im_data_rbuf.row_ptr(row);
200         for(int col=0;col<width;col++){
201             int gdpix = gdImageGetTrueColorPixel(img,col,row);
202             //extract the alpha value from the pixel
203             int gdpixalpha = ((gdpix) & 0x7F000000) >> 24;
204             int alpha;
205             //treat the specific transparent case
206             //(we never can set alpha to 0 with newalpha=255-2*oldalpha)
207             if(gdpixalpha==127)
208                 alpha=0;
209             else
210                 alpha=255-(gdpixalpha<<1);
211             //reinject the corrected alpha value in the pixel and image
212             rowptr[col]=((gdpix)&0x00FFFFFF)|(alpha<<24);           
213         }
214     }
215     return im_data_rbuf;
216 }
217
218 ///base rendering class for AGG.
219 ///creates the AGG structures used later for rendering.
220 ///the allocation of these structures does take some time and memory, so
221 ///this object should be cached and not created each time a shape has to be drawn
222 class AGGMapserverRenderer {
223 public:
224     AGGMapserverRenderer(mapserv_row_ptr_cache<int>  *ptr) :
225         pRowCache(ptr),
226         thePixelFormat(*pRowCache),
227         thePixelFormat_pre(*pRowCache),
228         ren_base(thePixelFormat),
229         ren_base_pre(thePixelFormat_pre),
230         ren_aa(ren_base),
231         m_fman(m_feng)
232         {
233            
234         }
235    
236     ~AGGMapserverRenderer() {
237         delete pRowCache;
238     }
239     ///clear the whole image.
240     ///@param color the fully opaque background color to use 
241     void clear(colorObj *color) {
242         ren_base.clear(msToAGGColor(color));
243     }
244    
245     ///
246     ///clear the background of the image. sets the background to fully transparent
247     void clear() {
248         ren_base.clear(agg::rgba(0,0,0,0));
249     }
250    
251     ///shortcut function to render an ellipse, optinally filled and/or outlined
252     ///@param x,y the center of the ellipse
253     ///@param w,h the width and height of the ellipse, aligned with the image principal axes
254     ///@param color the fill color of the ellipse, or NULL for no fill
255     ///@param outlinecolor the color of the outline, or NULL for no outline
256     ///@param outlinewidth the width of the optional outline
257     void renderEllipse(double x, double y, double w, double h, colorObj *color,
258             colorObj *outlinecolor, double outlinewidth=1) {
259         agg::path_storage path;
260         agg::ellipse ellipse(x,y,w/2.,h/2.0);
261         path.concat_path(ellipse);
262         renderPathSolid(path,color,outlinecolor,outlinewidth);     
263     }
264    
265     ///render a polyline, optionally dashed
266     ///@param p the path_storage containing the vertexes of the polyline
267     ///@param c the color of the line
268     ///@param width the width of the polyline
269     ///@param dashstylelength optional length of the dashing parameters.
270     ///     set to 0 for no dashes
271     ///@param dashstyle array containing the dashing parameters. it's length must be
272     ///     greater or eauql to dashstylelength
273     ///@param lc the style of the line caps, defaults to round caps
274     ///@param lj the style of the line joins, defaults to round joins
275     void renderPolyline(agg::path_storage &p,colorObj *c,
276             double width,int dashstylelength, int *dashstyle,
277             enum agg::line_cap_e lc=agg::round_cap,
278             enum agg::line_join_e lj=agg::round_join) {
279         ras_aa.reset();
280         ras_aa.filling_rule(agg::fill_non_zero);
281         ren_aa.color(msToAGGColor(c));
282
283         if (dashstylelength <= 0) {
284             agg::conv_stroke<agg::path_storage> stroke(p); 
285             stroke.width(width);
286             stroke.line_cap(lc);
287             stroke.line_join(lj);
288             ras_aa.add_path(stroke);
289         } else {
290             agg::conv_dash<agg::path_storage> dash(p);
291             agg::conv_stroke<agg::conv_dash<agg::path_storage> > stroke_dash(dash); 
292             for (int i=0; i<dashstylelength; i+=2) {
293                 if (i < dashstylelength-1)
294                     dash.add_dash(dashstyle[i], dashstyle[i+1]);
295             }
296             stroke_dash.width(width);
297             stroke_dash.line_cap(lc);
298             stroke_dash.line_join(lj);                       
299             ras_aa.add_path(stroke_dash);
300         }
301         agg::render_scanlines(ras_aa, sl, ren_aa);     
302     }
303    
304     ///brush a polyline with a vector symbol. draws the vector symbol in a temporary
305     ///image that will be used as a brush for rendering the polyline. this function
306     ///doesn't do any actual rendering, it only creates the brush and forwards to the
307     ///brush rendering function.
308     ///@param shape the polyline to brush
309     ///@param symbol the shape of the vector symbol used for brushing,
310     ///     can be a stroke or a simple path
311     ///@param tilewidth,tileheight size of the brush to be created
312     ///@param color the color the vector symbol should be drawn with
313     ///@param backgroundcolor optional background color of the tile, or
314     ///     NULL to render the symbol on a transparent background
315     template<class VertexSource1, class VertexSource2>
316     void renderPolylineVectorSymbol(VertexSource1 &shape, VertexSource2 &symbol,
317             int tilewidth, int tileheight,
318             colorObj *color, colorObj *backgroundcolor) {
319         ras_aa.reset();
320         ras_aa.filling_rule(agg::fill_non_zero);
321         typedef agg::wrap_mode_repeat wrap_type;
322         typedef agg::image_accessor_wrap<GDpixfmt,wrap_type,wrap_type> img_source_type;
323         typedef agg::span_pattern_rgba<img_source_type> span_gen_type;
324         agg::int8u*           m_pattern;
325         agg::rendering_buffer m_pattern_rbuf;
326         m_pattern = new agg::int8u[tilewidth * tileheight * 4];
327         m_pattern_rbuf.attach(m_pattern, tilewidth,tileheight, tilewidth*4);
328
329         GDpixfmt pixf(m_pattern_rbuf);
330         agg::renderer_base<GDpixfmt> rb(pixf);
331         agg::renderer_scanline_aa_solid<agg::renderer_base<GDpixfmt> > rs(rb);
332
333         if(backgroundcolor!=NULL && MS_VALID_COLOR(*backgroundcolor))
334             rb.clear(msToAGGColor(backgroundcolor));
335         else
336             rb.clear(agg::rgba(0,0,0,0));
337         rs.color(msToAGGColor(color));
338         ras_aa.add_path(symbol);
339         agg::render_scanlines(ras_aa, sl, rs);
340         renderPathPixmapBGRA(shape,pixf);
341         delete[](m_pattern);
342     }
343    
344     ///brush a line.
345     ///@param line the line to brush
346     ///@param pattern a pixmap to use as a brush. this pattern must be in a
347     ///     premultiplied state, i.e. each color has been divided by its alpha (mapped to 0..1).
348     ///     images produced by the AGG renderers are already premultiplied, but images
349     ///     read from an another source (i.e the GD pixmaps) should be explicitely
350     ///     premultiplied before calling
351     void renderPathPixmapBGRA(agg::path_storage &line, GDpixfmt &pattern) {
352         agg::pattern_filter_bilinear_rgba8 fltr;
353         typedef agg::line_image_pattern<agg::pattern_filter_bilinear_rgba8> pattern_type;
354         typedef agg::renderer_outline_image<renderer_base_pre, pattern_type> renderer_img_type;
355         typedef agg::rasterizer_outline_aa<renderer_img_type, agg::line_coord_sat> rasterizer_img_type;
356         pattern_type patt(fltr); 
357        
358         patt.create(pattern);
359         renderer_img_type ren_img(ren_base_pre, patt);
360         rasterizer_img_type ras_img(ren_img);
361         ras_img.add_path(line);
362     }
363
364     ///render a shape represented by an agg::path_storage
365     ///@param path the path containing the geometry to render
366     ///@param color fill color or null for no fill
367     ///@param outlinecolor outline color or null for no outline
368     ///@param outlinewidth width of outline
369     ///@param lc capping used for the optional outline, defaults to round caps
370     ///@param lj joins used for the optional outline, defaults to round joins
371     void renderPathSolid(agg::path_storage &path, colorObj *color,
372             colorObj *outlinecolor, double outlinewidth,
373             enum agg::line_cap_e lc=agg::round_cap,
374             enum agg::line_join_e lj=agg::round_join) {
375         ras_aa.reset();
376         if(color!=NULL && MS_VALID_COLOR(*color)) {
377             //use this to preserve holes in the geometry
378             //this is needed when drawing fills
379             ras_aa.filling_rule(agg::fill_even_odd);
380             ras_aa.add_path ( path );
381             ren_aa.color(msToAGGColor(color));
382             agg::render_scanlines ( ras_aa, sl, ren_aa );
383         }
384         if(outlinecolor!=NULL && MS_VALID_COLOR(*outlinecolor) && outlinewidth > 0) {
385             ras_aa.reset();
386             ras_aa.filling_rule(agg::fill_non_zero);
387             ren_aa.color(msToAGGColor(outlinecolor));
388             agg::conv_stroke<agg::path_storage> stroke(path);
389             stroke.width(outlinewidth);
390             stroke.line_cap(lc);
391             stroke.line_join(lj);
392             ras_aa.add_path(stroke);
393             agg::render_scanlines ( ras_aa, sl, ren_aa );
394         }
395     }
396
397     ///render a fill pattern clipped by a shape
398     ///this has only been tested when pattern is a stroke, and clipper a path.
399     ///used for hatches. note that the pattern is not repeated or tiled
400     ///@param pattern the pattern to use as the fill
401     ///@param clipper the polygon to use as a clipper. only the pixels inside
402     ///     this clipper will be affected by the pattern
403     ///@param color the color used to render the pattern
404     template<class VertexSource1, class VertexSource2>
405     void renderPathSolidClipped(VertexSource1 &pattern, VertexSource2 &clipper,
406             colorObj *color)
407     {
408         if(color==NULL || !MS_VALID_COLOR(*color))
409             return;
410         agg::rasterizer_scanline_aa<> ras1,ras2;
411         agg::scanline_storage_aa8 storage;
412         agg::scanline_storage_aa8 storage1;
413         agg::scanline_storage_aa8 storage2;
414         agg::scanline_p8 sl1,sl2;
415         ras1.filling_rule(agg::fill_non_zero);
416         ras1.add_path(pattern);                   
417         agg::render_scanlines(ras1, sl, storage1);
418         ras2.filling_rule(agg::fill_even_odd);
419         ras2.add_path(clipper);
420         agg::render_scanlines(ras2,sl,storage2);
421         agg::sbool_combine_shapes_aa(agg::sbool_and, storage1, storage2, sl1, sl2, sl, storage);
422         ren_aa.color(msToAGGColor(color));
423         agg::render_scanlines ( storage, sl, ren_aa );
424     }
425    
426     ///tile a shape with a vector symbol. this function
427     ///doesn't do any actual rendering, it only creates the tile and forwards to the
428     ///tiling rendering function.
429     ///@param shape the polygon to tile
430     ///@param tile the shape of the vector symbol used as a tile,
431     ///     can be a stroke or a simple path
432     ///@param tilewidth,tileheight size of the tile to be created
433     ///@param color the color the vector symbol should be drawn with
434     ///@param backgroundcolor optional background color of the tile, or
435     ///     NULL to render the symbol on a transparent background
436     ///TODO: add the outlinecolor
437     template<class VertexSource1, class VertexSource2>
438     void renderPathTiled(VertexSource1 &shape, VertexSource2 &tile,
439             int tilewidth, int tileheight, colorObj *color, colorObj *backgroundcolor)
440     {
441         ras_aa.reset();
442         ras_aa.filling_rule(agg::fill_non_zero);
443         agg::int8u*           m_pattern;
444         agg::rendering_buffer m_pattern_rbuf;
445         m_pattern = new agg::int8u[tilewidth * tileheight * 4];
446         m_pattern_rbuf.attach(m_pattern, tilewidth,tileheight, tilewidth*4);
447
448         GDpixfmt pixf(m_pattern_rbuf);
449         agg::renderer_base<GDpixfmt> rb(pixf);
450         agg::renderer_scanline_aa_solid<agg::renderer_base<GDpixfmt> > rs(rb);
451         if(backgroundcolor!=NULL && MS_VALID_COLOR(*backgroundcolor))
452             rb.clear(msToAGGColor(backgroundcolor));
453         else
454             rb.clear(agg::rgba(0,0,0,0));
455         rs.color(msToAGGColor(color));
456         ras_aa.add_path(tile);
457         agg::render_scanlines(ras_aa, sl, rs);
458         renderPathTiledPixmapBGRA(shape,pixf);
459         delete[](m_pattern);
460     }
461     ///tile a shape with a truetype character. this function
462     ///doesn't do any actual rendering, it only creates the tile and forwards to the
463     ///tiling rendering function.
464     ///@param shape the polygon to tile
465     ///@param font full path of the truetype font file to use
466     ///@param glyphUnicode the code of the caracter used
467     ///@param glyphheight size of the character to be rendered
468     ///@param gap size in pixels between each rendered character
469     ///     (applied vertically and horizontally)
470     ///@param color the color the character
471     ///@param backgroundcolor optional background color of the tile, or
472     ///     NULL to render the symbol on a transparent background
473     ///@param outlinecolor optional color of a one pixel width outline drawn around
474     ///     the character, or NULL for no outline       
475     template<class VertexSource1>
476             void renderPathTruetypeTiled(VertexSource1 &shape, char *font, int glyphUnicode,
477                     double glyphheight, double gap, colorObj *color, colorObj *backgroundcolor,
478                     colorObj *outlinecolor)
479         {
480         if(!m_feng.load_font(font, 0, agg::glyph_ren_outline))
481             return;
482         m_feng.hinting(true);
483         m_feng.height(glyphheight);
484         m_feng.resolution(96);
485         m_feng.flip_y(true);
486         font_curve_type m_curves(m_fman.path_adaptor());
487         const agg::glyph_cache* glyph=m_fman.glyph(glyphUnicode);
488         if(!glyph) return;
489         int gw=glyph->bounds.x2-glyph->bounds.x1,
490             gh=glyph->bounds.y2-glyph->bounds.y1;
491         int tilewidth=MS_NINT(gw+gap)+1,
492             tileheight=MS_NINT(gh+gap)+1;
493        
494             ras_aa.filling_rule(agg::fill_non_zero);
495             agg::int8u* m_pattern;
496             agg::rendering_buffer m_pattern_rbuf;
497             m_pattern = new agg::int8u[tilewidth * tileheight * 4];
498             m_pattern_rbuf.attach(m_pattern, tilewidth,tileheight, tilewidth*4);
499
500             GDpixfmt pixf(m_pattern_rbuf);
501             agg::renderer_base<GDpixfmt> rb(pixf);
502             agg::renderer_scanline_aa_solid<agg::renderer_base<GDpixfmt> > rs(rb);
503             if(backgroundcolor!=NULL && MS_VALID_COLOR(*backgroundcolor))
504                 rb.clear(msToAGGColor(backgroundcolor));
505             else
506                 rb.clear(agg::rgba(0,0,0,0));
507            
508             double fy=(tileheight+gh)/2.;
509             double fx=(tilewidth-gw)/2.;
510             if(outlinecolor!=NULL && MS_VALID_COLOR(*outlinecolor)) {
511                 ras_aa.reset();
512                 m_fman.init_embedded_adaptors(glyph,fx,fy);
513                 for(int i=-1;i<=1;i++)
514                     for(int j=-1;j<=1;j++) {
515                         if(i||j) {
516                             agg::trans_affine_translation tr(i,j);
517                             agg::conv_transform<font_curve_type, agg::trans_affine> trans_c(m_curves, tr);
518                             ras_aa.add_path(trans_c);
519                         }
520                     }
521                 rs.color(msToAGGColor(outlinecolor));
522                 agg::render_scanlines(ras_aa, sl, rs);
523             }
524             if(color!=NULL && MS_VALID_COLOR(*color)) {
525                 ras_aa.reset();
526                 m_fman.init_embedded_adaptors(glyph,fx,fy);
527                 ras_aa.add_path(m_curves);
528                 rs.color(msToAGGColor(color));
529                 agg::render_scanlines(ras_aa, sl, rs);
530             }
531             renderPathTiledPixmapBGRA(shape,pixf);
532             delete[](m_pattern);
533         }
534    
535    
536     ///tile a path with a pixmap symbol. the pixmap is repeated in each direction
537     ///@param path the polygon to fill
538     ///@param tile a pixmap to use as a tile. this pixmap must be in a
539     ///     premultiplied state, i.e. each color has been divided by its alpha (mapped to 0..1).
540     ///     images produced by the AGG renderers are already premultiplied, but images
541     ///     read from an another source (i.e the GD pixmaps) should be explicitely
542     ///     premultiplied before calling
543     template<class VertexSource1>
544     void renderPathTiledPixmapBGRA(VertexSource1 &path ,GDpixfmt &tile) {
545         typedef agg::wrap_mode_repeat wrap_type;
546         typedef agg::image_accessor_wrap<GDpixfmt,wrap_type,wrap_type> img_source_type;
547         typedef agg::span_pattern_rgba<img_source_type> span_gen_type;
548         agg::span_allocator<agg::rgba8> sa;
549         ras_aa.reset();
550         ras_aa.filling_rule(agg::fill_even_odd);
551         img_source_type img_src(tile);
552         span_gen_type sg(img_src, 0, 0);
553         ras_aa.add_path(path);
554         agg::render_scanlines_aa(ras_aa, sl, ren_base_pre, sa, sg);
555     }
556
557     ///render a single pixmap at a specified location
558     ///@param img_pixf the pixmap to render. must be premultiplied
559     ///@param x,y where to render the pixmap. this is the point where the
560     ///     center of the pixmap will be placed
561     ///@param angle,scale angle and scale for rendering the pixmap. angle is in degrees.
562     ///     if one of these values is meaningfull (i.e angle!=360 or 0, scale!=1), bilinear
563     ///     filtering will be applied. if not the image is copied without subpixel positioning
564     ///     (i.e. at the nearest integer position)to avoid the blur caused by the bilinear filtering
565     void renderPixmapBGRA(GDpixfmt &img_pixf, double x, double y, double angle, double scale) {
566         ras_aa.reset();
567         ras_aa.filling_rule(agg::fill_non_zero);
568        
569         if((angle!=0 && angle!=360) || scale !=1) {
570             agg::trans_affine image_mtx;
571             image_mtx *= agg::trans_affine_translation(-(double)img_pixf.width()/2.,
572                     -(double)img_pixf.height()/2.);
573             image_mtx *= agg::trans_affine_rotation(angle * agg::pi / 180.0);
574             image_mtx *= agg::trans_affine_scaling(scale);
575
576
577
578             image_mtx*= agg::trans_affine_translation(x,y);
579             image_mtx.invert();
580             typedef agg::span_interpolator_linear<> interpolator_type;
581             interpolator_type interpolator(image_mtx);
582             agg::span_allocator<agg::rgba8> sa;
583
584             // "hardcoded" bilinear filter
585             //------------------------------------------
586             typedef agg::span_image_filter_rgba_bilinear_clip<GDpixfmt, interpolator_type> span_gen_type;
587             span_gen_type sg(img_pixf, agg::rgba(0,0,0,0), interpolator);
588             agg::path_storage pixmap_bbox;
589             int ims_2 = MS_NINT(MS_MAX(img_pixf.height(),img_pixf.width())*scale*1.415)/2+1;
590             pixmap_bbox.move_to(x-ims_2,y-ims_2);
591             pixmap_bbox.line_to(x+ims_2,y-ims_2);
592             pixmap_bbox.line_to(x+ims_2,y+ims_2);
593             pixmap_bbox.line_to(x-ims_2,y+ims_2);
594             pixmap_bbox.close_polygon();     
595             ras_aa.add_path(pixmap_bbox);
596             agg::render_scanlines_aa(ras_aa, sl, ren_base_pre, sa, sg);
597         }
598         else {
599             //just copy the image at the correct location (we place the pixmap on
600             //the nearest integer pixel to avoid blurring)
601             ren_base_pre.blend_from(img_pixf,0,MS_NINT(x-img_pixf.width()/2.),MS_NINT(y-img_pixf.height()/2.));
602         }
603     }
604    
605     ///render a freetype string
606     ///@param x,y the lower left corner where to start the string, or the center of
607     ///     the character if isMarker is true
608     ///@param color the font color
609     ///@param outlinecolor optional color used for outlining the text
610     ///@param size height in pixels of the font
611     ///@param font full path of the truetype font to use
612     ///@param thechars null terminated string to render
613     ///@param angle angle to use. in radians
614     ///@param shadowcolor, shdx,shdy color and offset of an optional shadow. defaults
615     ///     to being offset one pixel to the lower right.
616     ///@param isMarker defines if the text should have its lower left corner start at the
617     ///     given x,y (false), or if the text should be centered on x,y (true).
618     ///     when set to true, will only center the text if this one is a single character
619     int renderGlyphs(double x, double y, colorObj *color, colorObj *outlinecolor,
620             double size, char *font, char *thechars, double angle=0,
621             colorObj *shadowcolor=NULL, double shdx=1, double shdy=1,
622             bool isMarker=false, bool isUTF8Encoded=false) {
623        
624         ras_aa.filling_rule(agg::fill_non_zero);
625         agg::trans_affine mtx;
626         mtx *= agg::trans_affine_translation(-x,-y);
627         mtx *= agg::trans_affine_rotation(-angle);
628         mtx *= agg::trans_affine_translation(x,y);
629        
630         if(!m_feng.load_font(font, 0, agg::glyph_ren_outline))
631         {
632             return MS_FAILURE;
633         }
634        
635         m_feng.hinting(true);
636         m_feng.height(size);
637         //m_feng.width(size);
638         m_feng.resolution(96);
639         m_feng.flip_y(true);
640         font_curve_type m_curves(m_fman.path_adaptor());
641         const agg::glyph_cache* glyph;
642        
643         if(isMarker) {
644             /* adjust center wrt the size of the glyph
645              * bounds are given in integer coordinates around (0,0)
646              * the y axis is flipped
647              */
648             glyph=m_fman.glyph(thechars[0]);
649             x-=glyph->bounds.x1+(glyph->bounds.x2-glyph->bounds.x1)/2.;
650             y+=-glyph->bounds.y2+ (glyph->bounds.y2-glyph->bounds.y1)/2.;
651         }
652         int unicode;
653         double fx=x,fy=y;
654         const char *utfptr=thechars;
655         agg::path_storage glyphs;
656        
657         //first render all the glyphs to a path
658         while(*utfptr) {
659             if(*utfptr=='\r') {fx=x;utfptr++;continue;}
660             if(*utfptr=='\n') {fx=x;fy+=ceil(size*LINESPACE);utfptr++;continue;}
661             if(isUTF8Encoded)
662                 utfptr+=msUTF8ToUniChar(utfptr, &unicode);
663             else {
664                 unicode=(int)((unsigned char)utfptr[0]);
665                 utfptr++;
666             }
667             glyph=m_fman.glyph(unicode);;
668             if(glyph)
669             {
670                 m_fman.init_embedded_adaptors(glyph,fx,fy);
671                 agg::conv_transform<font_curve_type, agg::trans_affine> trans_c(m_curves, mtx);
672                 glyphs.concat_path(trans_c);
673                 fx += glyph->advance_x;
674                 fy += glyph->advance_y;
675             }
676         }
677        
678         //use a smoother renderer for the shadow
679         if(shadowcolor!=NULL && MS_VALID_COLOR(*shadowcolor)) {
680             agg::trans_affine_translation tr(shdx,shdy);
681             agg::conv_transform<agg::path_storage, agg::trans_affine> tglyphs(glyphs,tr);
682             agg::line_profile_aa prof;
683             prof.width(0.5);
684             renderer_oaa ren_oaa(ren_base,prof);
685             rasterizer_outline_aa rasterizer_smooth(ren_oaa);
686             ren_oaa.color(msToAGGColor(shadowcolor));
687             rasterizer_smooth.add_path(tglyphs);
688         }
689         if(outlinecolor!=NULL && MS_VALID_COLOR(*outlinecolor)) {
690             ras_aa.reset();
691             ras_aa.filling_rule(agg::fill_non_zero);
692             //draw the text offset by one pixel in each direction (NW,W,SW,N,S,NE,E,SE)
693             for(int i=-1;i<=1;i++) {
694                 for(int j=-1;j<=1;j++) {
695                     if(i||j) {
696                         agg::trans_affine_translation tr(i,j);
697                         agg::conv_transform<agg::path_storage, agg::trans_affine> tglyphs(glyphs,tr);
698                         ras_aa.add_path(tglyphs);
699                     }
700                 }
701             }
702             ren_aa.color(msToAGGColor(outlinecolor));
703             agg::render_scanlines(ras_aa, sl, ren_aa);
704         }
705    
706         if(color!=NULL && MS_VALID_COLOR(*color)) {
707             ras_aa.reset();
708             ras_aa.filling_rule(agg::fill_non_zero);
709             ras_aa.add_path(glyphs);
710             ren_aa.color(msToAGGColor(color));
711             agg::render_scanlines(ras_aa, sl, ren_aa);
712         }
713         return MS_SUCCESS;
714     }
715
716 private:
717     mapserv_row_ptr_cache<int>  *pRowCache;
718     pixelFormat thePixelFormat;
719     pixelFormat_pre thePixelFormat_pre;
720     renderer_base ren_base;
721     renderer_base_pre ren_base_pre;
722     renderer_aa ren_aa;
723     scanline sl;
724     rasterizer_scanline ras_aa;
725     font_engine_type m_feng;
726     font_manager_type m_fman;
727     agg::rgba msToAGGColor(colorObj *c) {
728         return agg::rgba(((double) c->red) / 255.0,
729                 ((double) c->green) / 255.0,
730                 ((double) c->blue) / 255.0);
731     }
732
733
734 };
735
736 // ----------------------------------------------------------------------
737 // Utility function to create a GD image and its associated AGG renderer.
738 // Returns a pointer to a newly created imageObj structure.
739 // a pointer to the AGG renderer is stored in the imageObj, used for caching
740 // ----------------------------------------------------------------------
741 imageObj *msImageCreateAGG(int width, int height, outputFormatObj *format, char *imagepath, char *imageurl)
742 {
743     imageObj *pNewImage = NULL;
744
745     if(format->imagemode != MS_IMAGEMODE_RGB && format->imagemode != MS_IMAGEMODE_RGBA) {
746       msSetError(MS_AGGERR, "AGG driver only supports RGB or RGBA pixel models.", "msImageCreateAGG()");
747       return NULL;
748     }
749
750     pNewImage = msImageCreateGD(width, height, format, imagepath, imageurl);
751     if(!pNewImage)
752       return pNewImage;
753
754     mapserv_row_ptr_cache<int>  *pRowCache = new mapserv_row_ptr_cache<int>(pNewImage->img.gd);
755     if(! pRowCache) {
756       msSetError(MS_AGGERR, "Error binding GD image to AGG.", "msImageCreateAGG()");
757       return NULL;
758     }
759
760     AGGMapserverRenderer *ren = new AGGMapserverRenderer(pRowCache);
761     pNewImage->imageextra=(void *)ren;
762     return pNewImage;
763 }
764
765 ///internally used function to get the cached AGG renderer.
766 AGGMapserverRenderer* getAGGRenderer(imageObj *image) {
767     return (AGGMapserverRenderer*)image->imageextra;
768 }
769
770 // ----------------------------------------------------------------------
771 // Utility function to initialize the color of an image.  The background
772 // color is passed, but the outputFormatObj is consulted to see if the
773 // transparency should be set (for RGBA images).
774 // for the time being we fall back to GD
775 // ----------------------------------------------------------------------
776 void msImageInitAGG(imageObj *image, colorObj *background)
777 {
778     /*
779     // this is useless for the time being as the pixels are being overwritten by
780     // the call to msImageInitGD
781     // this block must be uncommented once we remove the call to msImageInitGD
782     AGGMapserverRenderer* ren = getAGGRenderer(image);
783     if(image->format->imagemode == MS_IMAGEMODE_RGBA) {
784         ren->clear();
785     } else {
786         ren->clear(background);
787     }
788     */
789     msImageInitGD(image, background);
790 }
791
792 // ------------------------------------------------------------------------
793 // Function to create a custom hatch symbol based on an arbitrary angle.
794 // ------------------------------------------------------------------------
795 static agg::path_storage createHatchAGG(int sx, int sy, double angle,