1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  MapServer
5  * Purpose:  Rendering utility functions
6  * Author:   Thomas Bonfort and the MapServer team.
7  *
8  ******************************************************************************
9  * Copyright (c) 1996-2011 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 "mapcopy.h"
32 #include "fontcache.h"
33 
computeSymbolStyle(symbolStyleObj * s,styleObj * src,symbolObj * symbol,double scalefactor,double resolutionfactor)34 void computeSymbolStyle(symbolStyleObj *s, styleObj *src, symbolObj *symbol, double scalefactor,
35     double resolutionfactor)
36 {
37   double default_size;
38   double target_size;
39   double style_size;
40 
41   default_size = msSymbolGetDefaultSize(symbol);
42   style_size = (src->size==-1)?default_size:src->size;
43 
44   INIT_SYMBOL_STYLE(*s);
45   if(symbol->type == MS_SYMBOL_PIXMAP) {
46     s->color = s->outlinecolor = NULL;
47   } else if(symbol->filled || symbol->type == MS_SYMBOL_TRUETYPE) {
48     if(MS_VALID_COLOR(src->color))
49       s->color = &src->color;
50     if(MS_VALID_COLOR(src->outlinecolor))
51       s->outlinecolor = &src->outlinecolor;
52   } else {
53     if(MS_VALID_COLOR(src->color))
54       s->outlinecolor = &(src->color);
55     else if(MS_VALID_COLOR(src->outlinecolor))
56       s->outlinecolor = &(src->outlinecolor);
57     s->color = NULL;
58   }
59 
60 
61   if(MS_VALID_COLOR(src->backgroundcolor)) {
62     s->backgroundcolor = &(src->backgroundcolor);
63   }
64 
65   target_size = style_size * scalefactor;
66   target_size = MS_MAX(target_size, src->minsize*resolutionfactor);
67   target_size = MS_MIN(target_size, src->maxsize*resolutionfactor);
68   s->scale = target_size / default_size;
69   s->gap = src->gap * target_size / style_size;
70 
71   if(s->outlinecolor) {
72     s->outlinewidth =  src->width * scalefactor;
73     s->outlinewidth = MS_MAX(s->outlinewidth, src->minwidth*resolutionfactor);
74     s->outlinewidth = MS_MIN(s->outlinewidth, src->maxwidth*resolutionfactor);
75   } else {
76     s->outlinewidth = 0;
77   }
78 
79   s->rotation = src->angle * MS_DEG_TO_RAD;
80 }
81 
82 
83 #define COMPARE_COLORS(a,b) (\
84     ((a).red==(b).red) && \
85     ((a).green==(b).green) && \
86     ((a).blue==(b).blue) && \
87     ((a).alpha==(b).alpha))
88 
searchTileCache(imageObj * img,symbolObj * symbol,symbolStyleObj * s,int width,int height)89 tileCacheObj *searchTileCache(imageObj *img, symbolObj *symbol, symbolStyleObj *s, int width, int height)
90 {
91   tileCacheObj *cur = img->tilecache;
92   while(cur != NULL) {
93     if( cur->width == width
94         && cur->height == height
95         && cur->symbol == symbol
96         && cur->outlinewidth == s->outlinewidth
97         && cur->rotation == s->rotation
98         && cur->scale == s->scale
99         && (!s->color || COMPARE_COLORS(cur->color,*s->color))
100         && (!s->backgroundcolor || COMPARE_COLORS(cur->backgroundcolor,*s->backgroundcolor))
101         && (!s->outlinecolor || COMPARE_COLORS(cur->outlinecolor,*s->outlinecolor)))
102       return cur;
103     cur = cur->next;
104   }
105   return NULL;
106 }
107 
preloadSymbol(symbolSetObj * symbolset,symbolObj * symbol,rendererVTableObj * renderer)108 int preloadSymbol(symbolSetObj *symbolset, symbolObj *symbol, rendererVTableObj *renderer) {
109   switch(symbol->type) {
110   case MS_SYMBOL_VECTOR:
111   case MS_SYMBOL_ELLIPSE:
112   case MS_SYMBOL_SIMPLE:
113   case (MS_SYMBOL_TRUETYPE):
114     break;
115   case (MS_SYMBOL_SVG):
116 #if defined(USE_SVG_CAIRO) || defined(USE_RSVG)
117     return msPreloadSVGSymbol(symbol);
118 #else
119     msSetError(MS_SYMERR, "SVG symbol support is not enabled.", "preloadSymbol()");
120     return MS_FAILURE;
121 #endif
122     break;
123   case (MS_SYMBOL_PIXMAP): {
124     if(!symbol->pixmap_buffer) {
125       if(MS_SUCCESS != msPreloadImageSymbol(renderer,symbol))
126         return MS_FAILURE;
127     }
128   }
129   break;
130   default:
131     msSetError(MS_MISCERR,"unsupported symbol type %d", "preloadSymbol()", symbol->type);
132     return MS_FAILURE;
133   }
134   return MS_SUCCESS;
135 }
136 
137 /* add a cached tile to the current image's cache */
addTileCache(imageObj * img,imageObj * tile,symbolObj * symbol,symbolStyleObj * style,int width,int height)138 tileCacheObj *addTileCache(imageObj *img,
139                            imageObj *tile, symbolObj *symbol, symbolStyleObj *style, int width, int height)
140 {
141   tileCacheObj *cachep;
142 
143   if(img->ntiles >= MS_IMAGECACHESIZE) { /* remove last element, size stays the same */
144     cachep = img->tilecache;
145 
146     /*go to the before last cache object*/
147     while(cachep->next && cachep->next->next) cachep = cachep->next;
148 
149     /*free the last tile's data*/
150     msFreeImage(cachep->next->image);
151 
152     /*reuse the last tile object*/
153     /* make the cache point to the start of the list*/
154     cachep->next->next = img->tilecache;
155     /* point the global cache to the new first */
156     img->tilecache = cachep->next;
157     /* the before last cache is now last, so it has no successor*/
158     cachep->next = NULL;
159 
160   } else {
161     img->ntiles += 1;
162     cachep = (tileCacheObj*)malloc(sizeof(tileCacheObj));
163     MS_CHECK_ALLOC(cachep, sizeof(tileCacheObj), NULL);
164     cachep->next = img->tilecache;
165     img->tilecache = cachep;
166   }
167 
168   cachep = img->tilecache;
169 
170   cachep->image = tile;
171   cachep->outlinewidth = style->outlinewidth;
172   cachep->scale = style->scale;
173   cachep->rotation = style->rotation;
174   cachep->outlinewidth = style->outlinewidth;
175   if(style->color) MS_COPYCOLOR(&cachep->color,style->color);
176   if(style->outlinecolor) MS_COPYCOLOR(&cachep->outlinecolor,style->outlinecolor);
177   if(style->backgroundcolor) MS_COPYCOLOR(&cachep->backgroundcolor,style->backgroundcolor);
178   cachep->width = width;
179   cachep->height = height;
180   cachep->symbol = symbol;
181   return(cachep);
182 }
183 
184 /* helper function to center glyph on the desired point */
drawGlyphMarker(imageObj * img,face_element * face,glyph_element * glyphc,double px,double py,int size,double rotation,colorObj * clr,colorObj * oclr,int olwidth)185 int WARN_UNUSED drawGlyphMarker(imageObj *img, face_element *face, glyph_element *glyphc, double px, double py, int size, double rotation,
186     colorObj *clr, colorObj *oclr, int olwidth)
187 {
188   double ox, oy;
189   textPathObj path;
190   glyphObj glyph;
191   rendererVTableObj *renderer = img->format->vtable;
192   if(!renderer->renderGlyphs) return MS_FAILURE;
193   path.numglyphs = 1;
194   glyph.glyph = glyphc;
195   glyph.face = face;
196   path.glyphs = &glyph;
197   path.glyph_size = size;
198   glyph.rot = rotation;
199   ox = (glyphc->metrics.maxx + glyphc->metrics.minx) / 2.0;
200   oy = (glyphc->metrics.maxy + glyphc->metrics.miny) / 2.0;
201   if(rotation != 0) {
202     double sina, cosa;
203     double rox,roy;
204     sina = sin(rotation);
205     cosa = cos(rotation);
206     rox = ox * cosa - oy * sina;
207     roy = ox * sina + oy * cosa;
208     px -= rox;
209     py += roy;
210     glyph.pnt.x = px;
211     glyph.pnt.y = py;
212   } else {
213     glyph.pnt.x = px - ox;
214     glyph.pnt.y = py + oy;
215   }
216   return renderer->renderGlyphs(img, &path, clr, oclr, olwidth, MS_TRUE);
217 }
218 
219 
getTile(imageObj * img,symbolObj * symbol,symbolStyleObj * s,int width,int height,int seamlessmode)220 imageObj *getTile(imageObj *img, symbolObj *symbol,  symbolStyleObj *s, int width, int height,
221                   int seamlessmode)
222 {
223   tileCacheObj *tile;
224   int status = MS_SUCCESS;
225   rendererVTableObj *renderer = img->format->vtable;
226   if(width==-1 || height == -1) {
227     width=height=MS_MAX(symbol->sizex,symbol->sizey);
228   }
229   tile = searchTileCache(img,symbol,s,width,height);
230 
231   if(tile==NULL) {
232     imageObj *tileimg;
233     double p_x,p_y;
234     tileimg = msImageCreate(width,height,img->format,NULL,NULL,img->resolution, img->resolution, NULL);
235     if(UNLIKELY(!tileimg)) {
236       return NULL;
237     }
238     if(!seamlessmode) {
239       p_x = width/2.0;
240       p_y = height/2.0;
241       switch(symbol->type) {
242         case (MS_SYMBOL_TRUETYPE):
243         {
244           unsigned int unicode;
245           glyph_element *glyphc;
246           face_element *face = msGetFontFace(symbol->font, &img->map->fontset);
247           if(UNLIKELY(!face)) { status = MS_FAILURE; break; }
248           msUTF8ToUniChar(symbol->character, &unicode);
249           unicode = msGetGlyphIndex(face,unicode);
250           glyphc = msGetGlyphByIndex(face, MS_MAX(MS_NINT(s->scale),1), unicode);
251           if(UNLIKELY(!glyphc)) { status = MS_FAILURE; break; }
252           status = drawGlyphMarker(tileimg, face, glyphc, p_x, p_y, s->scale, s->rotation,
253                 s->color, s->outlinecolor, s->outlinewidth);
254         }
255           break;
256         case (MS_SYMBOL_PIXMAP):
257           status = msPreloadImageSymbol(renderer,symbol);
258           if(UNLIKELY(status == MS_FAILURE)) { break; }
259           status = renderer->renderPixmapSymbol(tileimg, p_x, p_y, symbol, s);
260           break;
261         case (MS_SYMBOL_ELLIPSE):
262           status = renderer->renderEllipseSymbol(tileimg, p_x, p_y,symbol, s);
263           break;
264         case (MS_SYMBOL_VECTOR):
265           status = renderer->renderVectorSymbol(tileimg, p_x, p_y, symbol, s);
266           break;
267 
268         case (MS_SYMBOL_SVG):
269 #if defined(USE_SVG_CAIRO) || defined(USE_RSVG)
270           status = msPreloadSVGSymbol(symbol);
271           if(LIKELY(status == MS_SUCCESS)) {
272             if (renderer->supports_svg) {
273               status = renderer->renderSVGSymbol(tileimg, p_x, p_y, symbol, s);
274             } else {
275               status = msRenderRasterizedSVGSymbol(tileimg,p_x,p_y,symbol, s);
276             }
277           }
278 #else
279           msSetError(MS_SYMERR, "SVG symbol support is not enabled.", "getTile()");
280           status = MS_FAILURE;
281 #endif
282           break;
283         default:
284           msSetError(MS_SYMERR, "Unknown symbol type %d", "getTile()", symbol->type);
285           status = MS_FAILURE;
286           break;
287       }
288       if(UNLIKELY(status == MS_FAILURE)) {
289         msFreeImage(tileimg);
290         return NULL;
291       }
292     } else {
293       /*
294        * in seamless mode, we render the the symbol 9 times on a 3x3 grid to account for
295        * antialiasing blending from one tile to the next. We finally keep the center tile
296        */
297       imageObj *tile3img = msImageCreate(width*3,height*3,img->format,NULL,NULL,
298                                          img->resolution, img->resolution, NULL);
299       int i,j;
300       rasterBufferObj tmpraster;
301       for(i=1; i<=3; i++) {
302         p_x = (i+0.5)*width;
303         for(j=1; j<=3; j++) {
304           p_y = (j+0.5) * height;
305           switch(symbol->type) {
306             case (MS_SYMBOL_TRUETYPE):
307             {
308               unsigned int unicode;
309               glyph_element *glyphc;
310               face_element *face = msGetFontFace(symbol->font, &img->map->fontset);
311               if(UNLIKELY(!face)) { status = MS_FAILURE; break; }
312               msUTF8ToUniChar(symbol->character, &unicode);
313               unicode = msGetGlyphIndex(face,unicode);
314               glyphc = msGetGlyphByIndex(face, MS_MAX(MS_NINT(s->scale),1), unicode);
315               if(UNLIKELY(!glyphc)) { status = MS_FAILURE; break; }
316               status = drawGlyphMarker(tileimg, face, glyphc, p_x, p_y, s->scale, s->rotation,
317                     s->color, s->outlinecolor, s->outlinewidth);
318             }
319               break;
320             case (MS_SYMBOL_PIXMAP):
321               status = msPreloadImageSymbol(renderer,symbol);
322               if(UNLIKELY(status == MS_FAILURE)) { break; }
323               status = renderer->renderPixmapSymbol(tile3img, p_x, p_y, symbol, s);
324               break;
325             case (MS_SYMBOL_ELLIPSE):
326               status = renderer->renderEllipseSymbol(tile3img, p_x, p_y,symbol, s);
327               break;
328             case (MS_SYMBOL_VECTOR):
329               status = renderer->renderVectorSymbol(tile3img, p_x, p_y, symbol, s);
330               break;
331             default:
332               msSetError(MS_SYMERR, "BUG: Seamless mode is only for vector symbols", "getTile()");
333               return NULL;
334           }
335           if(UNLIKELY(status == MS_FAILURE)) {
336             msFreeImage(tile3img);
337             return NULL;
338           }
339         }
340       }
341       if(UNLIKELY(status == MS_FAILURE)) {
342         msFreeImage(tile3img);
343         return NULL;
344       }
345 
346       status = MS_IMAGE_RENDERER(tile3img)->getRasterBufferHandle(tile3img,&tmpraster);
347       if(UNLIKELY(status == MS_FAILURE)) {
348         msFreeImage(tile3img);
349         return NULL;
350       }
351       status = renderer->mergeRasterBuffer(tileimg,
352                                   &tmpraster,
353                                   1.0,width,height,0,0,width,height
354                                  );
355       msFreeImage(tile3img);
356     }
357     if(UNLIKELY(status == MS_FAILURE)) {
358       msFreeImage(tileimg);
359       return NULL;
360     }
361     tile = addTileCache(img,tileimg,symbol,s,width,height);
362   }
363   return tile->image;
364 }
365 
msImagePolylineMarkers(imageObj * image,shapeObj * p,symbolObj * symbol,symbolStyleObj * style,double spacing,double initialgap,int auto_angle)366 int msImagePolylineMarkers(imageObj *image, shapeObj *p, symbolObj *symbol,
367                            symbolStyleObj *style, double spacing,
368                            double initialgap, int auto_angle)
369 {
370   rendererVTableObj *renderer = MS_IMAGE_RENDERER(image);
371   int i,j;
372   pointObj point;
373   double original_rotation = style->rotation;
374   double symbol_width,symbol_height;
375   glyph_element *glyphc = NULL;
376   face_element *face = NULL;
377   int ret = MS_SUCCESS;
378   if(symbol->type != MS_SYMBOL_TRUETYPE) {
379     symbol_width = MS_MAX(1,symbol->sizex*style->scale);
380     symbol_height = MS_MAX(1,symbol->sizey*style->scale);
381   } else {
382     unsigned int unicode;
383     msUTF8ToUniChar(symbol->character, &unicode);
384     face = msGetFontFace(symbol->font, &image->map->fontset);
385     if(UNLIKELY(!face)) return MS_FAILURE;
386     unicode = msGetGlyphIndex(face,unicode);
387     glyphc = msGetGlyphByIndex(face, MS_MAX(MS_NINT(style->scale),1), unicode);
388     if(UNLIKELY(!glyphc)) return MS_FAILURE;
389     symbol_width = glyphc->metrics.maxx - glyphc->metrics.minx;
390     symbol_height = glyphc->metrics.maxy - glyphc->metrics.miny;
391   }
392   for(i=0; i<p->numlines; i++) {
393     int line_in = 0;
394     double line_length=0;
395     double current_length;
396     if(initialgap < 0) {
397       current_length = spacing/2.0; /* initial padding for each line */
398     } else {
399       current_length = initialgap; /* initial padding for each line */
400     }
401     for(j=1; j<p->line[i].numpoints; j++) {
402       double rx,ry,theta,length;
403       length = sqrt((pow((p->line[i].point[j].x - p->line[i].point[j-1].x),2) + pow((p->line[i].point[j].y - p->line[i].point[j-1].y),2)));
404       line_length += length;
405       if(length==0)continue;
406       rx = (p->line[i].point[j].x - p->line[i].point[j-1].x)/length;
407       ry = (p->line[i].point[j].y - p->line[i].point[j-1].y)/length;
408 
409       if (auto_angle) {
410         theta = asin(ry);
411         if(rx < 0) {
412           theta += MS_PI;
413         } else theta = -theta;
414         style->rotation = original_rotation + theta;
415       }
416       while (current_length <= length) {
417 
418         point.x = p->line[i].point[j - 1].x + current_length * rx;
419         point.y = p->line[i].point[j - 1].y + current_length * ry;
420         if(symbol->anchorpoint_x != 0.5 || symbol->anchorpoint_y != 0.5) {
421           double ox, oy;
422           ox = (0.5 - symbol->anchorpoint_x) * symbol_width;
423           oy = (0.5 - symbol->anchorpoint_y) * symbol_height;
424           if(style->rotation != 0) {
425             double sina,cosa;
426             double rox,roy;
427             sina = sin(-style->rotation);
428             cosa = cos(-style->rotation);
429             rox = ox * cosa - oy * sina;
430             roy = ox * sina + oy * cosa;
431             point.x += rox;
432             point.y += roy;
433           } else {
434             point.x += ox;
435             point.y += oy;
436           }
437         }
438 
439         /* if the point is not in the map extent, skip it. (POLYLINE_NO_CLIP) */
440         if ( (point.x < -(symbol_width) || point.x > (image->width+symbol_width)) ||
441              (point.y < -(symbol_height) || point.y > (image->height+symbol_height)) ) {
442           current_length += spacing;
443           line_in=1;
444           continue;
445         }
446 
447         switch (symbol->type) {
448           case MS_SYMBOL_PIXMAP:
449             ret = renderer->renderPixmapSymbol(image, point.x, point.y, symbol, style);
450             break;
451           case MS_SYMBOL_ELLIPSE:
452             ret = renderer->renderEllipseSymbol(image, point.x, point.y, symbol, style);
453             break;
454           case MS_SYMBOL_VECTOR:
455             ret = renderer->renderVectorSymbol(image, point.x, point.y, symbol, style);
456             break;
457           case MS_SYMBOL_TRUETYPE:
458             ret = drawGlyphMarker(image, face, glyphc, point.x, point.y, style->scale, style->rotation,
459                 style->color, style->outlinecolor, style->outlinewidth);
460             break;
461           case (MS_SYMBOL_SVG):
462 #if defined(USE_SVG_CAIRO) || defined(USE_RSVG)
463               if (renderer->supports_svg) {
464                 ret = renderer->renderSVGSymbol(image, point.x, point.y, symbol, style);
465               } else {
466                 ret = msRenderRasterizedSVGSymbol(image,point.x,point.y,symbol, style);
467               }
468 #else
469               msSetError(MS_SYMERR, "SVG symbol support is not enabled.", "msImagePolylineMarkers()()");
470               ret = MS_FAILURE;
471 #endif
472               break;
473         }
474         if( ret != MS_SUCCESS)
475           return ret;
476         current_length += spacing;
477         line_in=1;
478       }
479 
480       current_length -= length;
481     }
482 
483     /*
484      * if we couldn't place a symbol on the line and no initialgap was
485      * specified,  add one now we don't add the symbol if the line is shorter
486      * than the length of the symbol itself
487      */
488     if(initialgap < 0 && !line_in && line_length>symbol_width) {
489 
490       /* total lengths of beginnning and end of current segment */
491       double before_length=0,after_length=0;
492 
493       /*optimize*/
494       line_length /= 2.0;
495 
496       for(j=1; j<p->line[i].numpoints; j++) {
497         double length;
498         length = sqrt((pow((p->line[i].point[j].x - p->line[i].point[j-1].x),2) + pow((p->line[i].point[j].y - p->line[i].point[j-1].y),2)));
499         after_length += length;
500         if(after_length>line_length) {
501           double rx,ry,theta;
502           /* offset where the symbol should be drawn on the current
503            * segment */
504           double offset = line_length - before_length;
505 
506           rx = (p->line[i].point[j].x - p->line[i].point[j-1].x)/length;
507           ry = (p->line[i].point[j].y - p->line[i].point[j-1].y)/length;
508           if (auto_angle) {
509             theta = asin(ry);
510             if(rx < 0) {
511               theta += MS_PI;
512             } else theta = -theta;
513             style->rotation = original_rotation + theta;
514           }
515 
516           point.x = p->line[i].point[j - 1].x + offset * rx;
517           point.y = p->line[i].point[j - 1].y + offset * ry;
518           switch (symbol->type) {
519             case MS_SYMBOL_PIXMAP:
520               ret = renderer->renderPixmapSymbol(image, point.x, point.y, symbol, style);
521               break;
522             case MS_SYMBOL_ELLIPSE:
523               ret = renderer->renderEllipseSymbol(image, point.x, point.y, symbol, style);
524               break;
525             case MS_SYMBOL_VECTOR:
526               ret = renderer->renderVectorSymbol(image, point.x, point.y, symbol, style);
527               break;
528             case MS_SYMBOL_TRUETYPE:
529               ret = drawGlyphMarker(image, face, glyphc, point.x, point.y, style->scale, style->rotation,
530                   style->color, style->outlinecolor, style->outlinewidth);
531               break;
532             case (MS_SYMBOL_SVG):
533 #if defined(USE_SVG_CAIRO) || defined(USE_RSVG)
534               if (renderer->supports_svg) {
535                 ret = renderer->renderSVGSymbol(image, point.x, point.y, symbol, style);
536               } else {
537                 ret = msRenderRasterizedSVGSymbol(image,point.x,point.y,symbol, style);
538               }
539 #else
540               msSetError(MS_SYMERR, "SVG symbol support is not enabled.", "msImagePolylineMarkers()()");
541               ret = MS_FAILURE;
542 #endif
543               break;
544           }
545           break; /* we have rendered the single marker for this line */
546         }
547         before_length += length;
548       }
549     }
550 
551   }
552   return ret;
553 }
554 
msDrawLineSymbol(mapObj * map,imageObj * image,shapeObj * p,styleObj * style,double scalefactor)555 int msDrawLineSymbol(mapObj *map, imageObj *image, shapeObj *p,
556                      styleObj *style, double scalefactor)
557 {
558   int status = MS_SUCCESS;
559   if (image) {
560     if (MS_RENDERER_PLUGIN(image->format)) {
561       rendererVTableObj *renderer = image->format->vtable;
562       symbolObj *symbol;
563       shapeObj *offsetLine = p;
564       int i;
565       double width;
566       double finalscalefactor;
567 
568       if (p->numlines == 0)
569         return MS_SUCCESS;
570 
571       if (style->symbol >= map->symbolset.numsymbols || style->symbol < 0)
572         return MS_SUCCESS; /* no such symbol, 0 is OK   */
573 
574       symbol = map->symbolset.symbol[style->symbol];
575       /* store a reference to the renderer to be used for freeing */
576       symbol->renderer = renderer;
577 
578       width = style->width * scalefactor;
579       width = MS_MIN(width,style->maxwidth*image->resolutionfactor);
580       width = MS_MAX(width,style->minwidth*image->resolutionfactor);
581       if(style->width != 0) {
582         finalscalefactor = width / style->width;
583       } else {
584         finalscalefactor = 1.0;
585       }
586 
587       if(style->offsety==MS_STYLE_SINGLE_SIDED_OFFSET) {
588         offsetLine = msOffsetPolyline(p,style->offsetx * finalscalefactor ,MS_STYLE_SINGLE_SIDED_OFFSET);
589       } else if(style->offsety==MS_STYLE_DOUBLE_SIDED_OFFSET) {
590         offsetLine = msOffsetPolyline(p,style->offsetx * finalscalefactor ,MS_STYLE_DOUBLE_SIDED_OFFSET);
591       } else if(style->offsetx!=0 || style->offsety!=0) {
592         offsetLine = msOffsetPolyline(p, style->offsetx * finalscalefactor,
593                                       style->offsety * finalscalefactor);
594       }
595       if(style->symbol == 0 || (symbol->type==MS_SYMBOL_SIMPLE)) {
596         strokeStyleObj s;
597         s.linecap = style->linecap;
598         s.linejoin = style->linejoin;
599         s.linejoinmaxsize = style->linejoinmaxsize;
600         s.width = width;
601         s.patternlength = style->patternlength;
602         for(i=0; i<s.patternlength; i++)
603           s.pattern[i] = style->pattern[i] * finalscalefactor;
604         s.patternoffset = (style->initialgap<=0) ? 0 : (style->initialgap * finalscalefactor);
605 
606         if(MS_VALID_COLOR(style->color))
607           s.color = &style->color;
608         else if(MS_VALID_COLOR(style->outlinecolor))
609           s.color = &style->outlinecolor;
610         else {
611           /* msSetError(MS_MISCERR,"no color defined for line styling","msDrawLineSymbol()");
612            * not really an error */
613           status = MS_SUCCESS;
614           goto line_cleanup;
615         }
616         status = renderer->renderLine(image,offsetLine,&s);
617       } else {
618         symbolStyleObj s;
619         if(preloadSymbol(&map->symbolset, symbol, renderer) != MS_SUCCESS) {
620           status = MS_FAILURE;
621           goto line_cleanup;
622         }
623 
624         INIT_SYMBOL_STYLE(s);
625         computeSymbolStyle(&s,style,symbol,scalefactor,image->resolutionfactor);
626         s.style = style;
627 
628         /* compute a markerStyle and use it on the line */
629         if(style->gap<0) {
630           /* special function that treats any other symbol used as a marker, not a brush */
631           status = msImagePolylineMarkers(image,offsetLine,symbol,&s,-s.gap,
632                                  style->initialgap * finalscalefactor, 1);
633         } else if(style->gap>0) {
634           status = msImagePolylineMarkers(image,offsetLine,symbol,&s,s.gap,
635                                  style->initialgap * finalscalefactor,0);
636         } else {
637           if(renderer->renderLineTiled != NULL) {
638             int pw,ph;
639             imageObj* tile=NULL;
640             if(s.scale != 1) {
641               pw = MS_NINT(symbol->sizex * s.scale);
642               ph = MS_NINT(symbol->sizey * s.scale);
643             } else {
644               pw = symbol->sizex;
645               ph = symbol->sizey;
646             }
647             if(pw<1) pw=1;
648             if(ph<1) ph=1;
649             tile = getTile(image, symbol,&s,pw,ph,0);
650             status = renderer->renderLineTiled(image, offsetLine, tile);
651           } else {
652             msSetError(MS_RENDERERERR, "renderer does not support brushed lines", "msDrawLineSymbol()");
653             status = MS_FAILURE;
654           }
655         }
656       }
657 
658 line_cleanup:
659       if(offsetLine!=p) {
660         msFreeShape(offsetLine);
661         msFree(offsetLine);
662       }
663       return status;
664     } else if( MS_RENDERER_IMAGEMAP(image->format) )
665       msDrawLineSymbolIM(map, image, p, style, scalefactor);
666     else {
667       msSetError(MS_RENDERERERR, "unsupported renderer", "msDrawShadeSymbol()");
668       status = MS_FAILURE;
669     }
670   } else {
671     status = MS_FAILURE;
672   }
673   return status;
674 }
675 
msDrawShadeSymbol(mapObj * map,imageObj * image,shapeObj * p,styleObj * style,double scalefactor)676 int msDrawShadeSymbol(mapObj *map, imageObj *image, shapeObj *p, styleObj *style, double scalefactor)
677 {
678   int ret = MS_SUCCESS;
679   symbolObj *symbol;
680   if (!p)
681     return MS_SUCCESS;
682   if (p->numlines <= 0)
683     return MS_SUCCESS;
684 
685   if (style->symbol >= map->symbolset.numsymbols || style->symbol < 0)
686     return MS_SUCCESS; /* no such symbol, 0 is OK */
687   symbol = map->symbolset.symbol[style->symbol];
688 
689   /*
690    * if only an outlinecolor was defined, and not a color,
691    * switch to the line drawing function
692    *
693    * this behavior is kind of a mapfile hack, and must be
694    * kept for backwards compatibility
695    */
696   if (symbol->type != MS_SYMBOL_PIXMAP && symbol->type != MS_SYMBOL_SVG ) {
697     if (!MS_VALID_COLOR(style->color)) {
698       if(MS_VALID_COLOR(style->outlinecolor))
699         return msDrawLineSymbol(map, image, p, style, scalefactor);
700       else {
701         /* just do nothing if no color has been set */
702         return MS_SUCCESS;
703       }
704     }
705   }
706   if (image) {
707     if (MS_RENDERER_PLUGIN(image->format)) {
708       rendererVTableObj *renderer = image->format->vtable;
709       shapeObj *offsetPolygon = NULL;
710       /* store a reference to the renderer to be used for freeing */
711       if(style->symbol)
712         symbol->renderer = renderer;
713 
714       if (style->offsetx != 0 || style->offsety != 0) {
715         if(style->offsety==MS_STYLE_SINGLE_SIDED_OFFSET) {
716           offsetPolygon = msOffsetPolyline(p, style->offsetx*scalefactor, MS_STYLE_SINGLE_SIDED_OFFSET);
717         } else if(style->offsety==MS_STYLE_DOUBLE_SIDED_OFFSET) {
718           offsetPolygon = msOffsetPolyline(p,style->offsetx * scalefactor ,MS_STYLE_DOUBLE_SIDED_OFFSET);
719         } else {
720           offsetPolygon = msOffsetPolyline(p, style->offsetx*scalefactor,style->offsety*scalefactor);
721         }
722       } else {
723         offsetPolygon=p;
724       }
725       /* simple polygon drawing, without any specific symbol.
726        * also draws an optional outline */
727       if(style->symbol == 0 || symbol->type == MS_SYMBOL_SIMPLE) {
728         ret = renderer->renderPolygon(image,offsetPolygon,&style->color);
729         if(ret != MS_SUCCESS) goto cleanup;
730         if(MS_VALID_COLOR(style->outlinecolor)) {
731           strokeStyleObj s;
732           INIT_STROKE_STYLE(s);
733           s.color = &style->outlinecolor;
734           s.color->alpha = style->color.alpha;
735           s.width = (style->width == 0)?scalefactor:style->width*scalefactor;
736           s.width = MS_MIN(s.width, style->maxwidth);
737           s.width = MS_MAX(s.width, style->minwidth);
738           ret = renderer->renderLine(image,offsetPolygon,&s);
739         }
740         goto cleanup; /*finished plain polygon*/
741       } else if(symbol->type == MS_SYMBOL_HATCH) {
742         double width, spacing;
743         double pattern[MS_MAXPATTERNLENGTH];
744         int i;
745 
746         if(MS_VALID_COLOR(style->backgroundcolor)) {
747           ret = renderer->renderPolygon(image,offsetPolygon, &style->backgroundcolor);
748           if(ret != MS_SUCCESS) goto cleanup;
749         }
750         width = (style->width <= 0)?scalefactor:style->width*scalefactor;
751         width = MS_MIN(width, style->maxwidth*image->resolutionfactor);
752         width = MS_MAX(width, style->minwidth*image->resolutionfactor);
753         spacing = (style->size <= 0)?scalefactor:style->size*scalefactor;
754         spacing = MS_MIN(spacing, style->maxsize*image->resolutionfactor);
755         spacing = MS_MAX(spacing, style->minsize*image->resolutionfactor);
756 
757         /* scale the pattern by the factor applied to the width */
758         for(i=0; i<style->patternlength; i++) {
759           pattern[i] = style->pattern[i]*width/style->width;
760         }
761 
762         ret = msHatchPolygon(image,offsetPolygon,spacing,width,pattern,style->patternlength,style->angle, &style->color);
763         goto cleanup;
764       } else {
765         symbolStyleObj s;
766         int pw,ph;
767         imageObj *tile;
768         int seamless = 0;
769 
770         if(preloadSymbol(&map->symbolset,symbol,renderer) != MS_SUCCESS) {
771           ret = MS_FAILURE;
772           goto cleanup;
773         }
774 
775         INIT_SYMBOL_STYLE(s);
776         computeSymbolStyle(&s,style,symbol,scalefactor,image->resolutionfactor);
777         s.style = style;
778 
779         if (!s.color && !s.outlinecolor && symbol->type != MS_SYMBOL_PIXMAP && symbol->type != MS_SYMBOL_SVG) {
780           ret = MS_SUCCESS; /* nothing to do (colors are required except for PIXMAP symbols */
781           goto cleanup;
782         }
783 
784         if(s.backgroundcolor) {
785           ret = renderer->renderPolygon(image,offsetPolygon, s.backgroundcolor);
786           if(ret != MS_SUCCESS) goto cleanup;
787         }
788 
789         if(s.scale != 1) {
790           if (s.gap > 0) {
791             pw = MS_MAX(MS_NINT(s.gap),symbol->sizex * s.scale);
792             ph = MS_MAX(MS_NINT(s.gap),symbol->sizey * s.scale);
793           } else {
794             pw = MS_NINT(symbol->sizex * s.scale);
795             ph = MS_NINT(symbol->sizey * s.scale);
796           }
797         } else {
798           if (s.gap > 0) {
799             pw = MS_MAX(s.gap,symbol->sizex);
800             ph = MS_MAX(s.gap,symbol->sizey);
801           } else {
802             pw = symbol->sizex;
803             ph = symbol->sizey;
804           }
805         }
806         if(pw<1) pw=1;
807         if(ph<1) ph=1;
808 
809         /* if we're doing vector symbols with an antialiased pixel rendererer, we want to enable
810          * seamless mode, i.e. comute a tile that accounts for the blending of neighbouring
811          * tiles at the tile border
812          */
813         if(symbol->type == MS_SYMBOL_VECTOR && style->gap == 0 &&
814             (image->format->renderer == MS_RENDER_WITH_AGG ||
815              image->format->renderer == MS_RENDER_WITH_CAIRO_RASTER)) {
816           seamless = 1;
817         }
818         tile = getTile(image,symbol,&s,pw,ph,seamless);
819         ret = renderer->renderPolygonTiled(image,offsetPolygon, tile);
820       }
821 
822 cleanup:
823       if (offsetPolygon != p) {
824         msFreeShape(offsetPolygon);
825         msFree(offsetPolygon);
826       }
827       return ret;
828     } else if( MS_RENDERER_IMAGEMAP(image->format) )
829       msDrawShadeSymbolIM(map, image, p, style, scalefactor);
830   }
831   return ret;
832 }
833 
msDrawMarkerSymbol(mapObj * map,imageObj * image,pointObj * p,styleObj * style,double scalefactor)834 int msDrawMarkerSymbol(mapObj *map, imageObj *image, pointObj *p, styleObj *style,
835                        double scalefactor)
836 {
837   int ret = MS_SUCCESS;
838   if (!p)
839     return MS_SUCCESS;
840   if (style->symbol >= map->symbolset.numsymbols || style->symbol <= 0)
841     return MS_SUCCESS; /* no such symbol, 0 is OK   */
842 
843   if (image) {
844     if(MS_RENDERER_PLUGIN(image->format)) {
845       rendererVTableObj *renderer = image->format->vtable;
846       symbolStyleObj s;
847       double p_x,p_y;
848       symbolObj *symbol = map->symbolset.symbol[style->symbol];
849       /* store a reference to the renderer to be used for freeing */
850       symbol->renderer = renderer;
851       if(preloadSymbol(&map->symbolset,symbol,renderer) != MS_SUCCESS) {
852         return MS_FAILURE;
853       }
854 
855       computeSymbolStyle(&s,style,symbol,scalefactor,image->resolutionfactor);
856       s.style = style;
857       if (!s.color && !s.outlinecolor && symbol->type != MS_SYMBOL_PIXMAP &&
858           symbol->type != MS_SYMBOL_SVG) {
859         return MS_SUCCESS; // nothing to do if no color, except for pixmap symbols
860       }
861       if(s.scale == 0) {
862         return MS_SUCCESS;
863       }
864 
865 
866 
867       /* TODO: skip the drawing of the symbol if it's smaller than a pixel ?
868       if (s.size < 1)
869        return; // size too small
870        */
871 
872       p_x = p->x;
873       p_y = p->y;
874 
875       if (style->polaroffsetpixel != 0 ||
876           style->polaroffsetangle != 0) {
877         double angle = style->polaroffsetangle * MS_DEG_TO_RAD;
878         p_x +=  (style->polaroffsetpixel * cos(-angle)) * scalefactor;
879         p_y +=  (style->polaroffsetpixel * sin(-angle)) * scalefactor;
880       }
881 
882       p_x +=  style->offsetx * scalefactor;
883       p_y +=  style->offsety * scalefactor;
884 
885       if(symbol->anchorpoint_x != 0.5 || symbol->anchorpoint_y != 0.5) {
886         double sx,sy;
887         double ox, oy;
888         if(UNLIKELY(MS_FAILURE == msGetMarkerSize(map, style, &sx, &sy, scalefactor))) {
889           return MS_FAILURE;
890         }
891         ox = (0.5 - symbol->anchorpoint_x) * sx;
892         oy = (0.5 - symbol->anchorpoint_y) * sy;
893         if(s.rotation != 0) {
894           double sina, cosa;
895           double rox,roy;
896           sina = sin(-s.rotation);
897           cosa = cos(-s.rotation);
898           rox = ox * cosa - oy * sina;
899           roy = ox * sina + oy * cosa;
900           p_x += rox;
901           p_y += roy;
902         } else {
903           p_x += ox;
904           p_y += oy;
905         }
906       }
907 
908       if(renderer->use_imagecache) {
909         imageObj *tile = getTile(image, symbol, &s, -1, -1,0);
910         if(tile!=NULL)
911           return renderer->renderTile(image, tile, p_x, p_y);
912         else {
913           msSetError(MS_RENDERERERR, "problem creating cached tile", "msDrawMarkerSymbol()");
914           return MS_FAILURE;
915         }
916       }
917       switch (symbol->type) {
918         case (MS_SYMBOL_TRUETYPE): {
919           unsigned int unicode;
920           glyph_element *glyphc;
921           face_element *face = msGetFontFace(symbol->font, &map->fontset);
922           if(UNLIKELY(!face)) return MS_FAILURE;
923           msUTF8ToUniChar(symbol->character,&unicode);
924           unicode = msGetGlyphIndex(face,unicode);
925           glyphc = msGetGlyphByIndex(face, MS_MAX(MS_NINT(s.scale),1), unicode);
926           if(UNLIKELY(!glyphc)) return MS_FAILURE;
927           ret = drawGlyphMarker(image, face, glyphc, p_x, p_y, s.scale, s.rotation, s.color, s.outlinecolor, s.outlinewidth);
928         }
929         break;
930         case (MS_SYMBOL_PIXMAP): {
931           assert(symbol->pixmap_buffer);
932           ret = renderer->renderPixmapSymbol(image,p_x,p_y,symbol,&s);
933         }
934         break;
935         case (MS_SYMBOL_ELLIPSE): {
936           ret = renderer->renderEllipseSymbol(image, p_x, p_y,symbol, &s);
937         }
938         break;
939         case (MS_SYMBOL_VECTOR): {
940           ret = renderer->renderVectorSymbol(image, p_x, p_y, symbol, &s);
941         }
942         break;
943         case (MS_SYMBOL_SVG): {
944           if (renderer->supports_svg) {
945             ret = renderer->renderSVGSymbol(image, p_x, p_y, symbol, &s);
946           } else {
947 #if defined(USE_SVG_CAIRO) || defined(USE_RSVG)
948             ret = msRenderRasterizedSVGSymbol(image, p_x,p_y, symbol, &s);
949 #else
950             msSetError(MS_SYMERR, "SVG symbol support is not enabled.", "msDrawMarkerSymbol()");
951             return MS_FAILURE;
952 #endif
953           }
954         }
955         break;
956         default:
957           break;
958       }
959       return ret;
960     } else if( MS_RENDERER_IMAGEMAP(image->format) )
961       msDrawMarkerSymbolIM(map, image, p, style, scalefactor);
962 
963   }
964   return ret;
965 }
966 
967 
msDrawLabelBounds(mapObj * map,imageObj * image,label_bounds * bnds,styleObj * style,double scalefactor)968 int msDrawLabelBounds(mapObj *map, imageObj *image, label_bounds *bnds, styleObj *style, double scalefactor)
969 {
970   /*
971    * helper function to draw label bounds, where we might have only a rectObj and not
972    * a lineObj/shapeObj
973    */
974   shapeObj shape;
975   shape.numlines = 1;
976   if(bnds->poly) {
977     shape.line = bnds->poly;
978     return msDrawShadeSymbol(map,image,&shape,style,scalefactor);
979   } else {
980     pointObj pnts1[5];
981     lineObj l;
982     l.point = pnts1;
983     l.numpoints = 5;
984     pnts1[0].x = pnts1[1].x = pnts1[4].x = bnds->bbox.minx;
985     pnts1[2].x = pnts1[3].x = bnds->bbox.maxx;
986     pnts1[0].y = pnts1[3].y = pnts1[4].y = bnds->bbox.miny;
987     pnts1[1].y = pnts1[2].y = bnds->bbox.maxy;
988     shape.line = &l; // must return from this block
989     return msDrawShadeSymbol(map,image,&shape,style,scalefactor);
990   }
991 }
992 
msDrawTextSymbol(mapObj * map,imageObj * image,pointObj labelPnt,textSymbolObj * ts)993 int msDrawTextSymbol(mapObj *map, imageObj *image, pointObj labelPnt, textSymbolObj *ts)
994 {
995   rendererVTableObj *renderer = image->format->vtable;
996   colorObj *c = NULL, *oc = NULL;
997   int ow;
998   assert(ts->textpath);
999   if(!renderer->renderGlyphs) return MS_FAILURE;
1000 
1001   if(!ts->textpath->absolute) {
1002     int g;
1003     double cosa,sina;
1004     double x = labelPnt.x;
1005     double y = labelPnt.y;
1006     if(ts->rotation != 0) {
1007       cosa = cos(ts->rotation);
1008       sina = sin(ts->rotation);
1009       for(g=0;g<ts->textpath->numglyphs;g++) {
1010         double ox = ts->textpath->glyphs[g].pnt.x;
1011         double oy = ts->textpath->glyphs[g].pnt.y;
1012         ts->textpath->glyphs[g].pnt.x = ox * cosa + oy * sina + x;
1013         ts->textpath->glyphs[g].pnt.y = -ox * sina + oy * cosa + y;
1014         ts->textpath->glyphs[g].rot = ts->rotation;
1015       }
1016     } else {
1017       for(g=0;g<ts->textpath->numglyphs;g++) {
1018         ts->textpath->glyphs[g].pnt.x += x;
1019         ts->textpath->glyphs[g].pnt.y += y;
1020       }
1021     }
1022   }
1023 
1024   if(MS_VALID_COLOR(ts->label->shadowcolor)) {
1025     textSymbolObj *ts_shadow;
1026     int g;
1027     double ox, oy;
1028     double cosa,sina;
1029     int ret;
1030     if(ts->rotation != 0) {
1031       cosa = cos(ts->rotation);
1032       sina = sin(ts->rotation);
1033       ox = ts->scalefactor * (cosa * ts->label->shadowsizex +
1034           sina * ts->label->shadowsizey);
1035       oy = ts->scalefactor * (-sina * ts->label->shadowsizex +
1036           cosa * ts->label->shadowsizey);
1037     }
1038     else {
1039       ox = ts->scalefactor * ts->label->shadowsizex;
1040       oy = ts->scalefactor * ts->label->shadowsizey;
1041     }
1042 
1043     ts_shadow = msSmallMalloc(sizeof(textSymbolObj));
1044     initTextSymbol(ts_shadow);
1045     msCopyTextSymbol(ts_shadow,ts);
1046 
1047     for(g=0;g<ts_shadow->textpath->numglyphs;g++) {
1048       ts_shadow->textpath->glyphs[g].pnt.x += ox;
1049       ts_shadow->textpath->glyphs[g].pnt.y += oy;
1050     }
1051 
1052     ret = renderer->renderGlyphs(image,ts_shadow->textpath,&ts->label->shadowcolor,NULL,0, MS_FALSE);
1053     freeTextSymbol(ts_shadow);
1054     msFree(ts_shadow);
1055     if( ret != MS_SUCCESS )
1056       return ret;
1057   }
1058 
1059   if(MS_VALID_COLOR(ts->label->color))
1060     c = &ts->label->color;
1061   if(MS_VALID_COLOR(ts->label->outlinecolor))
1062     oc = &ts->label->outlinecolor;
1063   ow = MS_NINT((double)ts->label->outlinewidth * ((double)ts->textpath->glyph_size / (double)ts->label->size));
1064   return renderer->renderGlyphs(image,ts->textpath,c,oc,ow, MS_FALSE);
1065 
1066 }
1067 
1068 /************************************************************************/
1069 /*                          msCircleDrawLineSymbol                      */
1070 /*                                                                      */
1071 /************************************************************************/
msCircleDrawLineSymbol(mapObj * map,imageObj * image,pointObj * p,double r,styleObj * style,double scalefactor)1072 int msCircleDrawLineSymbol(mapObj *map, imageObj *image, pointObj *p, double r, styleObj *style, double scalefactor)
1073 {
1074   shapeObj *circle;
1075   int status;
1076   if (!image) return MS_FAILURE;
1077   circle = msRasterizeArc(p->x, p->y, r, 0, 360, 0);
1078   if (!circle) return MS_FAILURE;
1079   status = msDrawLineSymbol(map, image, circle, style, scalefactor);
1080   msFreeShape(circle);
1081   msFree(circle);
1082   return status;
1083 }
1084 
msCircleDrawShadeSymbol(mapObj * map,imageObj * image,pointObj * p,double r,styleObj * style,double scalefactor)1085 int msCircleDrawShadeSymbol(mapObj *map, imageObj *image, pointObj *p, double r, styleObj *style, double scalefactor)
1086 {
1087   shapeObj *circle;
1088   int status;
1089   if (!image) return MS_FAILURE;
1090   circle = msRasterizeArc(p->x, p->y, r, 0, 360, 0);
1091   if (!circle) return MS_FAILURE;
1092   status = msDrawShadeSymbol(map, image, circle, style, scalefactor);
1093   msFreeShape(circle);
1094   msFree(circle);
1095   return status;
1096 }
1097 
msDrawPieSlice(mapObj * map,imageObj * image,pointObj * p,styleObj * style,double radius,double start,double end)1098 int msDrawPieSlice(mapObj *map, imageObj *image, pointObj *p, styleObj *style, double radius, double start, double end)
1099 {
1100   int status;
1101   shapeObj *circle;
1102   double center_x = p->x;
1103   double center_y = p->y;
1104   if (!image) return MS_FAILURE;
1105   if(style->offsetx>0) {
1106     center_x+=style->offsetx*cos(((-start-end)*MS_PI/360.));
1107     center_y-=style->offsetx*sin(((-start-end)*MS_PI/360.));
1108   }
1109   circle = msRasterizeArc(center_x, center_y, radius, start, end, 1);
1110   if (!circle) return MS_FAILURE;
1111   status = msDrawShadeSymbol(map, image, circle, style, 1.0);
1112   msFreeShape(circle);
1113   msFree(circle);
1114   return status;
1115 }
1116 
1117 /*
1118  * RFC 49 implementation
1119  * if an outlinewidth is used:
1120  *  - augment the style's width to account for the outline width
1121  *  - swap the style color and outlinecolor
1122  *  - draw the shape (the outline) in the first pass of the
1123  *    caching mechanism
1124  */
1125 
msOutlineRenderingPrepareStyle(styleObj * pStyle,mapObj * map,layerObj * layer,imageObj * image)1126 void msOutlineRenderingPrepareStyle(styleObj *pStyle, mapObj *map, layerObj *layer, imageObj *image)
1127 {
1128   colorObj tmp;
1129 
1130   if (pStyle->outlinewidth > 0) {
1131     /* adapt width (must take scalefactor into account) */
1132     pStyle->width += (pStyle->outlinewidth / (layer->scalefactor/image->resolutionfactor)) * 2;
1133     pStyle->minwidth += pStyle->outlinewidth * 2;
1134     pStyle->maxwidth += pStyle->outlinewidth * 2;
1135     pStyle->size += (pStyle->outlinewidth/layer->scalefactor*(map->resolution/map->defresolution));
1136 
1137     /*swap color and outlinecolor*/
1138     tmp = pStyle->color;
1139     pStyle->color = pStyle->outlinecolor;
1140     pStyle->outlinecolor = tmp;
1141   }
1142 }
1143 
1144 /*
1145  * RFC 49 implementation: switch back the styleobj to its
1146  * original state, so the line fill will be drawn in the
1147  * second pass of the caching mechanism
1148  */
1149 
msOutlineRenderingRestoreStyle(styleObj * pStyle,mapObj * map,layerObj * layer,imageObj * image)1150 void msOutlineRenderingRestoreStyle(styleObj *pStyle, mapObj *map, layerObj *layer, imageObj *image)
1151 {
1152   colorObj tmp;
1153 
1154   if (pStyle->outlinewidth > 0) {
1155     /* reset widths to original state */
1156     pStyle->width -= (pStyle->outlinewidth / (layer->scalefactor/image->resolutionfactor)) * 2;
1157     pStyle->minwidth -= pStyle->outlinewidth * 2;
1158     pStyle->maxwidth -= pStyle->outlinewidth * 2;
1159     pStyle->size -= (pStyle->outlinewidth/layer->scalefactor*(map->resolution/map->defresolution));
1160 
1161     /*reswap colors to original state*/
1162     tmp = pStyle->color;
1163     pStyle->color = pStyle->outlinecolor;
1164     pStyle->outlinecolor = tmp;
1165   }
1166 }
1167