1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  MapServer
5  * Purpose:  High level msDrawMap() implementation and related functions.
6  * Author:   Steve Lime and the MapServer team.
7  *
8  ******************************************************************************
9  * Copyright (c) 1996-2005 Regents of the University of Minnesota.
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included in
19  * all copies of this Software or works derived from this Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  *****************************************************************************/
29 
30 #include <assert.h>
31 #include <math.h>
32 #include "mapserver.h"
33 #include "maptime.h"
34 #include "mapcopy.h"
35 #include "mapfile.h"
36 #include "mapows.h"
37 
38 
39 /* msPrepareImage()
40  *
41  * Returns a new imageObj ready for rendering the current map.
42  *
43  * If allow_nonsquare is set to MS_TRUE then the caller should call
44  * msMapRestoreRealExtent() once they are done with the image.
45  * This should be set to MS_TRUE only when called from msDrawMap(), see bug 945.
46  */
msPrepareImage(mapObj * map,int allow_nonsquare)47 imageObj *msPrepareImage(mapObj *map, int allow_nonsquare)
48 {
49   int i, status;
50   imageObj *image=NULL;
51   double geo_cellsize;
52 
53   if(map->width == -1 || map->height == -1) {
54     msSetError(MS_MISCERR, "Image dimensions not specified.", "msPrepareImage()");
55     return(NULL);
56   }
57 
58   msFreeLabelCache(&(map->labelcache));
59   msInitLabelCache(&(map->labelcache)); /* this clears any previously allocated cache */
60 
61   /* clear any previously created mask layer images */
62   for(i=0; i<map->numlayers; i++) {
63     if(GET_LAYER(map, i)->maskimage) {
64       msFreeImage(GET_LAYER(map, i)->maskimage);
65       GET_LAYER(map, i)->maskimage = NULL;
66     }
67   }
68 
69   status = msValidateContexts(map); /* make sure there are no recursive REQUIRES or LABELREQUIRES expressions */
70   if(status != MS_SUCCESS) return NULL;
71 
72   if(!map->outputformat) {
73     msSetError(MS_IMGERR, "Map outputformat not set!", "msPrepareImage()");
74     return(NULL);
75   } else if (MS_RENDERER_PLUGIN(map->outputformat)) {
76     rendererVTableObj *renderer = map->outputformat->vtable;
77     colorObj *bg = &map->imagecolor;
78     map->imagecolor.alpha=255;
79     if(map->transparent == MS_TRUE) {
80       /* don't set the image color */
81       bg = NULL;
82     }
83 
84   image = renderer->createImage(map->width, map->height, map->outputformat,bg);
85   if (image == NULL)
86     return(NULL);
87   image->format = map->outputformat;
88   image->format->refcount++;
89   image->width = map->width;
90   image->height = map->height;
91 
92   image->resolution = map->resolution;
93   image->resolutionfactor = map->resolution/map->defresolution;
94   if (map->web.imagepath)
95     image->imagepath = msStrdup(map->web.imagepath);
96   if (map->web.imageurl)
97     image->imageurl = msStrdup(map->web.imageurl);
98 
99   } else if( MS_RENDERER_IMAGEMAP(map->outputformat) ) {
100     image = msImageCreateIM(map->width, map->height, map->outputformat,
101                             map->web.imagepath, map->web.imageurl, map->resolution, map->defresolution);
102   } else if( MS_RENDERER_RAWDATA(map->outputformat) ) {
103     image = msImageCreate(map->width, map->height, map->outputformat,
104                           map->web.imagepath, map->web.imageurl, map->resolution, map->defresolution, &map->imagecolor);
105   } else {
106     image = NULL;
107   }
108 
109   if(!image) {
110     msSetError(MS_IMGERR, "Unable to initialize image.", "msPrepareImage()");
111     return(NULL);
112   }
113 
114   image->map = map;
115 
116   /*
117    * If we want to support nonsquare pixels, note that now, otherwise
118    * adjust the extent size to have square pixels.
119    *
120    * If allow_nonsquare is set to MS_TRUE then the caller should call
121    * msMapRestoreRealExtent() once they are done with the image.
122    * This should be set to MS_TRUE only when called from msDrawMap(), see bug 945.
123    */
124   if( allow_nonsquare && msTestConfigOption( map, "MS_NONSQUARE", MS_FALSE ) ) {
125     double cellsize_x = (map->extent.maxx - map->extent.minx)/map->width;
126     double cellsize_y = (map->extent.maxy - map->extent.miny)/map->height;
127 
128     if( cellsize_y != 0.0
129         && (fabs(cellsize_x/cellsize_y) > 1.00001
130             || fabs(cellsize_x/cellsize_y) < 0.99999) ) {
131       map->gt.need_geotransform = MS_TRUE;
132       if (map->debug)
133         msDebug( "msDrawMap(): kicking into non-square pixel preserving mode.\n" );
134     }
135     map->cellsize = (cellsize_x*0.5 + cellsize_y*0.5);
136   } else
137     map->cellsize = msAdjustExtent(&(map->extent),map->width,map->height);
138 
139   status = msCalculateScale(map->extent,map->units,map->width,map->height, map->resolution, &map->scaledenom);
140   if(status != MS_SUCCESS) {
141     msFreeImage(image);
142     return(NULL);
143   }
144 
145   /* update geotransform based on adjusted extent. */
146   msMapComputeGeotransform( map );
147 
148   /* Do we need to fake out stuff for rotated support? */
149   if( map->gt.need_geotransform )
150     msMapSetFakedExtent( map );
151 
152   /* We will need a cellsize that represents a real georeferenced */
153   /* coordinate cellsize here, so compute it from saved extents.   */
154 
155   geo_cellsize = map->cellsize;
156   if( map->gt.need_geotransform == MS_TRUE ) {
157     double cellsize_x = (map->saved_extent.maxx - map->saved_extent.minx)
158                         / map->width;
159     double cellsize_y = (map->saved_extent.maxy - map->saved_extent.miny)
160                         / map->height;
161 
162     geo_cellsize = sqrt(cellsize_x*cellsize_x + cellsize_y*cellsize_y)
163                    / sqrt(2.0);
164   }
165 
166   /* compute layer scale factors now */
167   for(i=0; i<map->numlayers; i++) {
168     if(GET_LAYER(map, i)->sizeunits != MS_PIXELS)
169       GET_LAYER(map, i)->scalefactor = (msInchesPerUnit(GET_LAYER(map, i)->sizeunits,0)/msInchesPerUnit(map->units,0)) / geo_cellsize;
170     else if(GET_LAYER(map, i)->symbolscaledenom > 0 && map->scaledenom > 0)
171       GET_LAYER(map, i)->scalefactor = GET_LAYER(map, i)->symbolscaledenom/map->scaledenom*map->resolution/map->defresolution;
172     else
173       GET_LAYER(map, i)->scalefactor = map->resolution/map->defresolution;
174   }
175 
176   image->refpt.x = MS_MAP2IMAGE_X_IC_DBL(0, map->extent.minx, 1.0/map->cellsize);
177   image->refpt.y = MS_MAP2IMAGE_Y_IC_DBL(0, map->extent.maxy, 1.0/map->cellsize);
178 
179   return image;
180 
181 }
182 
msCompositeRasterBuffer(mapObj * map,imageObj * img,rasterBufferObj * rb,LayerCompositer * comp)183 static int msCompositeRasterBuffer(mapObj *map, imageObj *img, rasterBufferObj *rb, LayerCompositer *comp) {
184   int ret = MS_SUCCESS;
185   if(MS_IMAGE_RENDERER(img)->compositeRasterBuffer) {
186     while(comp && ret == MS_SUCCESS) {
187       rasterBufferObj *rb_ptr = rb;
188       CompositingFilter *filter = comp->filter;
189       if(filter && comp->next) {
190        /* if we have another compositor to apply, then we need to copy the rasterBufferObj. Otherwise
191        * we can work on it directly */
192 	rb_ptr = (rasterBufferObj*)msSmallCalloc(sizeof(rasterBufferObj),1);
193 	msCopyRasterBuffer(rb_ptr,rb);
194       }
195       while(filter && ret == MS_SUCCESS) {
196         ret = msApplyCompositingFilter(map,rb_ptr,filter);
197         filter = filter->next;
198       }
199       if(ret == MS_SUCCESS)
200       	ret = MS_IMAGE_RENDERER(img)->compositeRasterBuffer(img,rb_ptr,comp->comp_op, comp->opacity);
201       if(rb_ptr != rb) {
202         msFreeRasterBuffer(rb_ptr);
203         msFree(rb_ptr);
204       }
205       comp = comp->next;
206     }
207   } else {
208     ret = MS_FAILURE;
209   }
210   return ret;
211 }
212 
213 /*
214  * Generic function to render the map file.
215  * The type of the image created is based on the imagetype parameter in the map file.
216  *
217  * mapObj *map - map object loaded in MapScript or via a mapfile to use
218  * int querymap - is this map the result of a query operation, MS_TRUE|MS_FALSE
219 */
msDrawMap(mapObj * map,int querymap)220 imageObj *msDrawMap(mapObj *map, int querymap)
221 {
222   int i;
223   layerObj *lp=NULL;
224   int status = MS_FAILURE;
225   imageObj *image = NULL;
226   struct mstimeval mapstarttime = {0}, mapendtime = {0};
227   struct mstimeval starttime = {0}, endtime = {0};
228 
229 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
230   enum MS_CONNECTION_TYPE lastconnectiontype;
231   httpRequestObj *pasOWSReqInfo=NULL;
232   int numOWSLayers=0;
233   int numOWSRequests=0;
234   wmsParamsObj sLastWMSParams;
235 #endif
236 
237   if(map->debug >= MS_DEBUGLEVEL_TUNING) msGettimeofday(&mapstarttime, NULL);
238 
239   if(querymap) { /* use queryMapObj image dimensions */
240     if(map->querymap.width != -1) map->width = map->querymap.width;
241     if(map->querymap.height != -1) map->height = map->querymap.height;
242   }
243 
244   msApplyMapConfigOptions(map);
245   image = msPrepareImage(map, MS_TRUE);
246 
247   if(!image) {
248     msSetError(MS_IMGERR, "Unable to initialize image.", "msDrawMap()");
249     return(NULL);
250   }
251 
252   if( map->debug >= MS_DEBUGLEVEL_DEBUG )
253     msDebug( "msDrawMap(): rendering using outputformat named %s (%s).\n",
254              map->outputformat->name,
255              map->outputformat->driver );
256 
257 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
258 
259   /* Time the OWS query phase */
260   if(map->debug >= MS_DEBUGLEVEL_TUNING ) msGettimeofday(&starttime, NULL);
261 
262   /* How many OWS (WMS/WFS) layers do we have to draw?
263    * Note: numOWSLayers is the number of actual layers and numOWSRequests is
264    * the number of HTTP requests which could be lower if multiple layers
265    * are merged into the same request.
266    */
267   numOWSLayers=0;
268   for(i=0; i<map->numlayers; i++) {
269       if(map->layerorder[i] == -1 )
270         continue;
271 
272       lp = GET_LAYER(map,map->layerorder[i]);
273       if( lp->connectiontype != MS_WMS &&
274           lp->connectiontype != MS_WFS ) {
275         continue;
276       }
277       numOWSLayers++;
278   }
279 
280   if (numOWSLayers > 0) {
281 
282     /* Alloc and init pasOWSReqInfo...
283      */
284     pasOWSReqInfo = (httpRequestObj *)malloc(numOWSLayers*sizeof(httpRequestObj));
285     if (pasOWSReqInfo == NULL) {
286       msSetError(MS_MEMERR, "Allocation of httpRequestObj failed.", "msDrawMap()");
287       return NULL;
288     }
289     msHTTPInitRequestObj(pasOWSReqInfo, numOWSLayers);
290     msInitWmsParamsObj(&sLastWMSParams);
291 
292     /* Pre-download all WMS/WFS layers in parallel before starting to draw map */
293     lastconnectiontype = MS_SHAPEFILE;
294     for(i=0; i<map->numlayers; i++) {
295       if(map->layerorder[i] == -1 )
296         continue;
297 
298       lp = GET_LAYER(map,map->layerorder[i]);
299       if( lp->connectiontype != MS_WMS &&
300           lp->connectiontype != MS_WFS ) {
301         continue;
302       }
303 
304       if( !msLayerIsVisible(map, lp) )
305         continue;
306 
307 #ifdef USE_WMS_LYR
308       if(lp->connectiontype == MS_WMS) {
309         if(msPrepareWMSLayerRequest(map->layerorder[i], map, lp, 1, lastconnectiontype, &sLastWMSParams, 0, 0, 0, NULL, pasOWSReqInfo, &numOWSRequests) == MS_FAILURE) {
310           msFreeWmsParamsObj(&sLastWMSParams);
311           msFreeImage(image);
312           msFree(pasOWSReqInfo);
313           return NULL;
314         }
315       }
316 #endif
317 
318 #ifdef USE_WFS_LYR
319       if(lp->connectiontype == MS_WFS) {
320         if(msPrepareWFSLayerRequest(map->layerorder[i], map, lp, pasOWSReqInfo, &numOWSRequests) == MS_FAILURE) {
321           msFreeWmsParamsObj(&sLastWMSParams);
322           msFreeImage(image);
323           msFree(pasOWSReqInfo);
324           return NULL;
325         }
326       }
327 #endif
328 
329       lastconnectiontype = lp->connectiontype;
330     }
331 
332     msFreeWmsParamsObj(&sLastWMSParams);
333   } /* if numOWSLayers > 0 */
334 
335   if(numOWSRequests && msOWSExecuteRequests(pasOWSReqInfo, numOWSRequests, map, MS_TRUE) == MS_FAILURE) {
336     msFreeImage(image);
337     msFree(pasOWSReqInfo);
338     return NULL;
339   }
340 
341   if(map->debug >= MS_DEBUGLEVEL_TUNING) {
342     msGettimeofday(&endtime, NULL);
343     msDebug("msDrawMap(): WMS/WFS set-up and query, %.3fs\n",
344             (endtime.tv_sec+endtime.tv_usec/1.0e6)-
345             (starttime.tv_sec+starttime.tv_usec/1.0e6) );
346   }
347 
348 #endif /* USE_WMS_LYR || USE_WFS_LYR */
349 
350   /* OK, now we can start drawing */
351   for(i=0; i<map->numlayers; i++) {
352 
353     if(map->layerorder[i] != -1) {
354       char *force_draw_label_cache = NULL;
355 
356       lp = (GET_LAYER(map,  map->layerorder[i]));
357 
358       if(lp->postlabelcache) /* wait to draw */
359         continue;
360 
361       if(map->debug >= MS_DEBUGLEVEL_TUNING || lp->debug >= MS_DEBUGLEVEL_TUNING ) msGettimeofday(&starttime, NULL);
362 
363       if(!msLayerIsVisible(map, lp)) continue;
364 
365       if(lp->connectiontype == MS_WMS) {
366 #ifdef USE_WMS_LYR
367         if(MS_RENDERER_PLUGIN(image->format) || MS_RENDERER_RAWDATA(image->format))
368           status = msDrawWMSLayerLow(map->layerorder[i], pasOWSReqInfo, numOWSRequests,  map, lp, image);
369         else {
370           msSetError(MS_WMSCONNERR, "Output format '%s' doesn't support WMS layers.", "msDrawMap()", image->format->name);
371           status = MS_FAILURE;
372         }
373 
374         if(status == MS_FAILURE) {
375           msSetError(MS_WMSCONNERR,
376                      "Failed to draw WMS layer named '%s'. This most likely happened because "
377                      "the remote WMS server returned an invalid image, and XML exception "
378                      "or another unexpected result in response to the GetMap request. Also check "
379                      "and make sure that the layer's connection URL is valid.",
380                      "msDrawMap()", lp->name);
381           msFreeImage(image);
382           msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
383           msFree(pasOWSReqInfo);
384           return(NULL);
385         }
386 
387 
388 #else /* ndef USE_WMS_LYR */
389         msSetError(MS_WMSCONNERR, "MapServer not built with WMS Client support, unable to render layer '%s'.", "msDrawMap()", lp->name);
390         msFreeImage(image);
391         return(NULL);
392 #endif
393       } else { /* Default case: anything but WMS layers */
394         if(querymap)
395           status = msDrawQueryLayer(map, lp, image);
396         else
397           status = msDrawLayer(map, lp, image);
398         if(status == MS_FAILURE) {
399           msSetError(MS_IMGERR, "Failed to draw layer named '%s'.", "msDrawMap()", lp->name);
400           msFreeImage(image);
401 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
402           if (pasOWSReqInfo) {
403             msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
404             msFree(pasOWSReqInfo);
405           }
406 #endif /* USE_WMS_LYR || USE_WFS_LYR */
407           return(NULL);
408         }
409       }
410       if(map->debug >= MS_DEBUGLEVEL_TUNING || lp->debug >= MS_DEBUGLEVEL_TUNING) {
411         msGettimeofday(&endtime, NULL);
412         msDebug("msDrawMap(): Layer %d (%s), %.3fs\n",
413                 map->layerorder[i], lp->name?lp->name:"(null)",
414                 (endtime.tv_sec+endtime.tv_usec/1.0e6)-
415                 (starttime.tv_sec+starttime.tv_usec/1.0e6) );
416       }
417 
418       /* Flush layer cache in-between layers if requested by PROCESSING directive*/
419       force_draw_label_cache = msLayerGetProcessingKey(lp, "FORCE_DRAW_LABEL_CACHE");
420       if (force_draw_label_cache &&
421 	  strncasecmp(force_draw_label_cache,"FLUSH",5)==0) {
422 	if(map->debug >= MS_DEBUGLEVEL_V)
423 	  msDebug("msDrawMap(): PROCESSING FORCE_DRAW_LABEL_CACHE=FLUSH found.\n");
424 	if(msDrawLabelCache(map, image) != MS_SUCCESS) {
425 	  msFreeImage(image);
426 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
427 	  if (pasOWSReqInfo) {
428 	    msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
429 	    msFree(pasOWSReqInfo);
430 	  }
431 #endif /* USE_WMS_LYR || USE_WFS_LYR */
432 	  return(NULL);
433 	}
434 	msFreeLabelCache(&(map->labelcache));
435 	msInitLabelCache(&(map->labelcache));
436       } /* PROCESSING FORCE_DRAW_LABEL_CACHE */
437 
438     }
439   }
440 
441   if(map->scalebar.status == MS_EMBED && !map->scalebar.postlabelcache) {
442 
443     /* We need to temporarily restore the original extent for drawing */
444     /* the scalebar as it uses the extent to recompute cellsize. */
445     if(map->gt.need_geotransform)
446       msMapRestoreRealExtent(map);
447 
448 
449     if(MS_SUCCESS != msEmbedScalebar(map, image)) {
450       msFreeImage( image );
451 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
452       /* Cleanup WMS/WFS Request stuff */
453       if (pasOWSReqInfo) {
454          msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
455          msFree(pasOWSReqInfo);
456       }
457 #endif
458       return NULL;
459     }
460 
461 
462     if(map->gt.need_geotransform)
463       msMapSetFakedExtent(map);
464   }
465 
466   if(map->legend.status == MS_EMBED && !map->legend.postlabelcache) {
467     if( msEmbedLegend(map, image) != MS_SUCCESS ) {
468       msFreeImage( image );
469 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
470       /* Cleanup WMS/WFS Request stuff */
471       if (pasOWSReqInfo) {
472          msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
473          msFree(pasOWSReqInfo);
474       }
475 #endif
476       return NULL;
477     }
478   }
479 
480   if(msDrawLabelCache(map, image) != MS_SUCCESS) {
481     msFreeImage(image);
482 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
483     if (pasOWSReqInfo) {
484       msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
485       msFree(pasOWSReqInfo);
486     }
487 #endif /* USE_WMS_LYR || USE_WFS_LYR */
488     return(NULL);
489   }
490 
491 
492   for(i=0; i<map->numlayers; i++) { /* for each layer, check for postlabelcache layers */
493 
494     lp = (GET_LAYER(map, map->layerorder[i]));
495 
496     if(!lp->postlabelcache) continue;
497     if(!msLayerIsVisible(map, lp)) continue;
498 
499     if(map->debug >= MS_DEBUGLEVEL_TUNING || lp->debug >= MS_DEBUGLEVEL_TUNING) msGettimeofday(&starttime, NULL);
500 
501     if(lp->connectiontype == MS_WMS) {
502 #ifdef USE_WMS_LYR
503       if(MS_RENDERER_PLUGIN(image->format) || MS_RENDERER_RAWDATA(image->format))
504         status = msDrawWMSLayerLow(map->layerorder[i], pasOWSReqInfo, numOWSRequests, map, lp, image);
505 
506 #else
507       status = MS_FAILURE;
508 #endif
509     } else {
510       if(querymap)
511         status = msDrawQueryLayer(map, lp, image);
512       else
513         status = msDrawLayer(map, lp, image);
514     }
515 
516     if(status == MS_FAILURE) {
517       msFreeImage(image);
518 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
519       if (pasOWSReqInfo) {
520         msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
521         msFree(pasOWSReqInfo);
522       }
523 #endif /* USE_WMS_LYR || USE_WFS_LYR */
524       return(NULL);
525     }
526 
527     if(map->debug >= MS_DEBUGLEVEL_TUNING || lp->debug >= MS_DEBUGLEVEL_TUNING) {
528       msGettimeofday(&endtime, NULL);
529       msDebug("msDrawMap(): Layer %d (%s), %.3fs\n",
530               map->layerorder[i], lp->name?lp->name:"(null)",
531               (endtime.tv_sec+endtime.tv_usec/1.0e6)-
532               (starttime.tv_sec+starttime.tv_usec/1.0e6) );
533     }
534 
535   }
536 
537   /* Do we need to fake out stuff for rotated support? */
538   /* This really needs to be done on every preceeding exit point too... */
539   if(map->gt.need_geotransform)
540     msMapRestoreRealExtent(map);
541 
542   if(map->legend.status == MS_EMBED && map->legend.postlabelcache)
543     if(UNLIKELY(MS_FAILURE == msEmbedLegend(map, image))) {
544       msFreeImage( image );
545 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
546       /* Cleanup WMS/WFS Request stuff */
547       if (pasOWSReqInfo) {
548         msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
549         msFree(pasOWSReqInfo);
550       }
551 #endif
552       return NULL;
553     }
554 
555   if(map->scalebar.status == MS_EMBED && map->scalebar.postlabelcache) {
556 
557     /* We need to temporarily restore the original extent for drawing */
558     /* the scalebar as it uses the extent to recompute cellsize. */
559     if(map->gt.need_geotransform)
560       msMapRestoreRealExtent(map);
561 
562     if(MS_SUCCESS != msEmbedScalebar(map, image)) {
563       msFreeImage( image );
564 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
565       /* Cleanup WMS/WFS Request stuff */
566       if (pasOWSReqInfo) {
567          msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
568          msFree(pasOWSReqInfo);
569       }
570 #endif
571       return NULL;
572     }
573 
574 
575     if(map->gt.need_geotransform)
576       msMapSetFakedExtent(map);
577   }
578 
579 #if defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
580   /* Cleanup WMS/WFS Request stuff */
581   if (pasOWSReqInfo) {
582     msHTTPFreeRequestObj(pasOWSReqInfo, numOWSRequests);
583     msFree(pasOWSReqInfo);
584   }
585 #endif
586 
587   if(map->debug >= MS_DEBUGLEVEL_TUNING) {
588     msGettimeofday(&mapendtime, NULL);
589     msDebug("msDrawMap() total time: %.3fs\n",
590             (mapendtime.tv_sec+mapendtime.tv_usec/1.0e6)-
591             (mapstarttime.tv_sec+mapstarttime.tv_usec/1.0e6) );
592   }
593 
594   return(image);
595 }
596 
597 /*
598  * Test whether a layer should be drawn or not in the current map view and
599  * at the current scale.
600  * Returns TRUE if layer is visible, FALSE if not.
601 */
msLayerIsVisible(mapObj * map,layerObj * layer)602 int msLayerIsVisible(mapObj *map, layerObj *layer)
603 {
604   int i;
605 
606   if(!layer->data && !layer->tileindex && !layer->connection && !layer->features && !layer->grid)
607     return(MS_FALSE); /* no data associated with this layer, not an error since layer may be used as a template from MapScript */
608 
609   if(layer->type == MS_LAYER_QUERY || layer->type == MS_LAYER_TILEINDEX) return(MS_FALSE);
610   if((layer->status != MS_ON) && (layer->status != MS_DEFAULT)) return(MS_FALSE);
611 
612   /* Do comparisons of layer scale vs map scale now, since msExtentsOverlap() */
613   /* can be slow */
614   if(map->scaledenom > 0) {
615 
616     /* layer scale boundaries should be checked first */
617     if((layer->maxscaledenom > 0) && (map->scaledenom > layer->maxscaledenom)) {
618       if( layer->debug >= MS_DEBUGLEVEL_V ) {
619         msDebug("msLayerIsVisible(): Skipping layer (%s) because LAYER.MAXSCALE is too small for this MAP scale\n", layer->name);
620       }
621       return(MS_FALSE);
622     }
623     if(/*(layer->minscaledenom > 0) &&*/ (map->scaledenom <= layer->minscaledenom)) {
624       if( layer->debug >= MS_DEBUGLEVEL_V ) {
625         msDebug("msLayerIsVisible(): Skipping layer (%s) because LAYER.MINSCALE is too large for this MAP scale\n", layer->name);
626       }
627       return(MS_FALSE);
628     }
629   }
630 
631   /* Only return MS_FALSE if it is definitely false. Sometimes it will return MS_UNKNOWN, which we
632   ** consider true, for this use case (it might be visible, try and draw it, see what happens). */
633   if ( msExtentsOverlap(map, layer) == MS_FALSE ) {
634     if( layer->debug >= MS_DEBUGLEVEL_V ) {
635       msDebug("msLayerIsVisible(): Skipping layer (%s) because LAYER.EXTENT does not intersect MAP.EXTENT\n", layer->name);
636     }
637     return(MS_FALSE);
638   }
639 
640   if(msEvalContext(map, layer, layer->requires) == MS_FALSE) return(MS_FALSE);
641 
642   if(map->scaledenom > 0) {
643 
644     /* now check class scale boundaries (all layers *must* pass these tests) */
645     if(layer->numclasses > 0) {
646       for(i=0; i<layer->numclasses; i++) {
647         if((layer->class[i]->maxscaledenom > 0) && (map->scaledenom > layer->class[i]->maxscaledenom))
648           continue; /* can skip this one, next class */
649         if((layer->class[i]->minscaledenom > 0) && (map->scaledenom <= layer->class[i]->minscaledenom))
650           continue; /* can skip this one, next class */
651 
652         break; /* can't skip this class (or layer for that matter) */
653       }
654       if(i == layer->numclasses) {
655         if( layer->debug >= MS_DEBUGLEVEL_V ) {
656           msDebug("msLayerIsVisible(): Skipping layer (%s) because no CLASS in the layer is in-scale for this MAP scale\n", layer->name);
657         }
658         return(MS_FALSE);
659       }
660     }
661 
662   }
663 
664   if (layer->maxscaledenom <= 0 && layer->minscaledenom <= 0) {
665     if((layer->maxgeowidth > 0) && ((map->extent.maxx - map->extent.minx) > layer->maxgeowidth)) {
666       if( layer->debug >= MS_DEBUGLEVEL_V ) {
667         msDebug("msLayerIsVisible(): Skipping layer (%s) because LAYER width is much smaller than map width\n", layer->name);
668       }
669       return(MS_FALSE);
670     }
671     if((layer->mingeowidth > 0) && ((map->extent.maxx - map->extent.minx) < layer->mingeowidth)) {
672       if( layer->debug >= MS_DEBUGLEVEL_V ) {
673         msDebug("msLayerIsVisible(): Skipping layer (%s) because LAYER width is much larger than map width\n", layer->name);
674       }
675       return(MS_FALSE);
676     }
677   }
678 
679   return MS_TRUE;  /* All tests passed.  Layer is visible. */
680 }
681 
682 
683 #define LAYER_NEEDS_COMPOSITING(layer) (((layer)->compositer != NULL) && ((layer)->compositer->next || (layer)->compositor->opacity < 100 || (layer)->compositor->compop != MS_COMPOP_SRC_OVER || (layer)->compositer->filter ))
684 /*
685  * Generic function to render a layer object.
686 */
msDrawLayer(mapObj * map,layerObj * layer,imageObj * image)687 int msDrawLayer(mapObj *map, layerObj *layer, imageObj *image)
688 {
689   imageObj *image_draw = image;
690   outputFormatObj *altFormat=NULL;
691   int retcode=MS_SUCCESS;
692   const char *alternativeFomatString = NULL;
693   layerObj *maskLayer = NULL;
694 
695   if(!msLayerIsVisible(map, layer))
696     return MS_SUCCESS;
697 
698   if(layer->compositer && !layer->compositer->next && layer->compositer->opacity == 0) return MS_SUCCESS; /* layer is completely transparent, skip it */
699 
700 
701   /* conditions may have changed since this layer last drawn, so retest
702      layer->project (Bug #673) */
703   layer->project = msProjectionsDiffer(&(layer->projection),&(map->projection));
704 
705   /* make sure labelcache setting is set correctly if postlabelcache is set. This is done by the parser but
706      may have been altered by a mapscript. see #5142 */
707   if(layer->postlabelcache) {
708     layer->labelcache = MS_FALSE;
709   }
710 
711   if(layer->mask) {
712     int maskLayerIdx;
713     /* render the mask layer in its own imageObj */
714     if(!MS_IMAGE_RENDERER(image)->supports_pixel_buffer) {
715       msSetError(MS_MISCERR, "Layer (%s) references references a mask layer, but the selected renderer does not support them", "msDrawLayer()",
716                  layer->name);
717       return (MS_FAILURE);
718     }
719     maskLayerIdx = msGetLayerIndex(map,layer->mask);
720     if(maskLayerIdx == -1) {
721       msSetError(MS_MISCERR, "Layer (%s) references unknown mask layer (%s)", "msDrawLayer()",
722                  layer->name,layer->mask);
723       return (MS_FAILURE);
724     }
725     maskLayer = GET_LAYER(map, maskLayerIdx);
726     if(!maskLayer->maskimage) {
727       int i;
728       int origstatus, origlabelcache;
729       altFormat =  msSelectOutputFormat(map, "png24");
730       msInitializeRendererVTable(altFormat);
731       /* TODO: check the png24 format hasn't been tampered with, i.e. it's agg */
732       maskLayer->maskimage= msImageCreate(image->width, image->height,altFormat,
733                                           image->imagepath, image->imageurl, map->resolution, map->defresolution, NULL);
734       if (!maskLayer->maskimage) {
735         msSetError(MS_MISCERR, "Unable to initialize mask image.", "msDrawLayer()");
736         return (MS_FAILURE);
737       }
738 
739       /*
740        * force the masked layer to status on, and turn off the labelcache so that
741        * eventual labels are added to the temporary image instead of being added
742        * to the labelcache
743        */
744       origstatus = maskLayer->status;
745       origlabelcache = maskLayer->labelcache;
746       maskLayer->status = MS_ON;
747       maskLayer->labelcache = MS_OFF;
748 
749       /* draw the mask layer in the temporary image */
750       retcode = msDrawLayer(map, maskLayer, maskLayer->maskimage);
751       maskLayer->status = origstatus;
752       maskLayer->labelcache = origlabelcache;
753       if(retcode != MS_SUCCESS) {
754         return MS_FAILURE;
755       }
756       /*
757        * hack to work around bug #3834: if we have use an alternate renderer, the symbolset may contain
758        * symbols that reference it. We want to remove those references before the altFormat is destroyed
759        * to avoid a segfault and/or a leak, and so the the main renderer doesn't pick the cache up thinking
760        * it's for him.
761        */
762       for(i=0; i<map->symbolset.numsymbols; i++) {
763         if (map->symbolset.symbol[i]!=NULL) {
764           symbolObj *s = map->symbolset.symbol[i];
765           if(s->renderer == MS_IMAGE_RENDERER(maskLayer->maskimage)) {
766             MS_IMAGE_RENDERER(maskLayer->maskimage)->freeSymbol(s);
767             s->renderer = NULL;
768           }
769         }
770       }
771       /* set the imagetype from the original outputformat back (it was removed by msSelectOutputFormat() */
772       msFree(map->imagetype);
773       map->imagetype = msStrdup(image->format->name);
774     }
775 
776   }
777   altFormat = NULL;
778   /* inform the rendering device that layer draw is starting. */
779   msImageStartLayer(map, layer, image);
780 
781 
782   /*check if an alternative renderer should be used for this layer*/
783   alternativeFomatString = msLayerGetProcessingKey( layer, "RENDERER");
784   if (MS_RENDERER_PLUGIN(image_draw->format) && alternativeFomatString!=NULL &&
785       (altFormat=  msSelectOutputFormat(map, alternativeFomatString))) {
786     rendererVTableObj *renderer=NULL;
787     msInitializeRendererVTable(altFormat);
788 
789     image_draw = msImageCreate(image->width, image->height,
790                                altFormat, image->imagepath, image->imageurl, map->resolution, map->defresolution, &map->imagecolor);
791     renderer = MS_IMAGE_RENDERER(image_draw);
792     renderer->startLayer(image_draw,map,layer);
793   } else if (MS_RENDERER_PLUGIN(image_draw->format)) {
794     rendererVTableObj *renderer = MS_IMAGE_RENDERER(image_draw);
795     if ((layer->mask && layer->connectiontype!=MS_WMS && layer->type != MS_LAYER_RASTER) || layer->compositer) {
796       /* masking occurs at the pixel/layer level for raster images, so we don't need to create a temporary image
797        in these cases
798        */
799       if (layer->mask || renderer->compositeRasterBuffer) {
800         image_draw = msImageCreate(image->width, image->height,
801                                    image->format, image->imagepath, image->imageurl, map->resolution, map->defresolution, NULL);
802         if (!image_draw) {
803           msSetError(MS_MISCERR, "Unable to initialize temporary transparent image.",
804                      "msDrawLayer()");
805           return (MS_FAILURE);
806         }
807         image_draw->map = map;
808         renderer->startLayer(image_draw,map,layer);
809       }
810     }
811   }
812   /*
813   ** redirect procesing of some layer types.
814   */
815   if(layer->connectiontype == MS_WMS) {
816 #ifdef USE_WMS_LYR
817     retcode = msDrawWMSLayer(map, layer, image_draw);
818 #else
819     retcode = MS_FAILURE;
820 #endif
821   } else if(layer->type == MS_LAYER_RASTER) {
822     retcode = msDrawRasterLayer(map, layer, image_draw);
823   } else if(layer->type == MS_LAYER_CHART) {
824     retcode = msDrawChartLayer(map, layer, image_draw);
825   } else {   /* must be a Vector layer */
826     retcode = msDrawVectorLayer(map, layer, image_draw);
827   }
828 
829   if (altFormat) {
830     rendererVTableObj *renderer = MS_IMAGE_RENDERER(image);
831     rendererVTableObj *altrenderer = MS_IMAGE_RENDERER(image_draw);
832     rasterBufferObj rb;
833     int i;
834     memset(&rb,0,sizeof(rasterBufferObj));
835 
836     altrenderer->endLayer(image_draw,map,layer);
837 
838     retcode = altrenderer->getRasterBufferHandle(image_draw,&rb);
839     if(UNLIKELY(retcode == MS_FAILURE)) {
840       goto altformat_cleanup;
841     }
842     retcode = renderer->mergeRasterBuffer(image,&rb,((layer->compositer)?(layer->compositer->opacity*0.01):(1.0)),0,0,0,0,rb.width,rb.height);
843     if(UNLIKELY(retcode == MS_FAILURE)) {
844       goto altformat_cleanup;
845     }
846 
847 altformat_cleanup:
848     /*
849      * hack to work around bug #3834: if we have use an alternate renderer, the symbolset may contain
850      * symbols that reference it. We want to remove those references before the altFormat is destroyed
851      * to avoid a segfault and/or a leak, and so the the main renderer doesn't pick the cache up thinking
852      * it's for him.
853      */
854     for(i=0; i<map->symbolset.numsymbols; i++) {
855       if (map->symbolset.symbol[i]!=NULL) {
856         symbolObj *s = map->symbolset.symbol[i];
857         if(s->renderer == altrenderer) {
858           altrenderer->freeSymbol(s);
859           s->renderer = NULL;
860         }
861       }
862     }
863     msFreeImage(image_draw);
864     /* set the imagetype from the original outputformat back (it was removed by msSelectOutputFormat() */
865     msFree(map->imagetype);
866     map->imagetype = msStrdup(image->format->name);
867   } else if( image != image_draw) {
868     rendererVTableObj *renderer = MS_IMAGE_RENDERER(image_draw);
869     rasterBufferObj rb;
870     memset(&rb,0,sizeof(rasterBufferObj));
871 
872     renderer->endLayer(image_draw,map,layer);
873 
874     retcode = renderer->getRasterBufferHandle(image_draw,&rb);
875     if(UNLIKELY(retcode == MS_FAILURE)) {
876       goto imagedraw_cleanup;
877     }
878     if(maskLayer && maskLayer->maskimage) {
879       rasterBufferObj mask;
880       unsigned int row,col;
881       memset(&mask,0,sizeof(rasterBufferObj));
882       retcode = MS_IMAGE_RENDERER(maskLayer->maskimage)->getRasterBufferHandle(maskLayer->maskimage,&mask);
883       if(UNLIKELY(retcode == MS_FAILURE)) {
884         goto imagedraw_cleanup;
885       }
886       /* modify the pixels of the overlay */
887 
888       if(rb.type == MS_BUFFER_BYTE_RGBA) {
889         for(row=0; row<rb.height; row++) {
890           unsigned char *ma,*a,*r,*g,*b;
891           r=rb.data.rgba.r+row*rb.data.rgba.row_step;
892           g=rb.data.rgba.g+row*rb.data.rgba.row_step;
893           b=rb.data.rgba.b+row*rb.data.rgba.row_step;
894           a=rb.data.rgba.a+row*rb.data.rgba.row_step;
895           ma=mask.data.rgba.a+row*mask.data.rgba.row_step;
896           for(col=0; col<rb.width; col++) {
897             if(*ma == 0) {
898               *a = *r = *g = *b = 0;
899             }
900             a+=rb.data.rgba.pixel_step;
901             r+=rb.data.rgba.pixel_step;
902             g+=rb.data.rgba.pixel_step;
903             b+=rb.data.rgba.pixel_step;
904             ma+=mask.data.rgba.pixel_step;
905           }
906         }
907       }
908     }
909     if(!layer->compositer) {
910       /*we have a mask layer with no composition configured, do a nomral blend */
911       retcode = renderer->mergeRasterBuffer(image,&rb,1.0,0,0,0,0,rb.width,rb.height);
912     } else {
913       retcode = msCompositeRasterBuffer(map,image,&rb,layer->compositer);
914     }
915     if(UNLIKELY(retcode == MS_FAILURE)) {
916       goto imagedraw_cleanup;
917     }
918 
919 imagedraw_cleanup:
920     msFreeImage(image_draw);
921   }
922 
923   msImageEndLayer(map,layer,image);
924   return(retcode);
925 }
926 
msDrawVectorLayer(mapObj * map,layerObj * layer,imageObj * image)927 int msDrawVectorLayer(mapObj *map, layerObj *layer, imageObj *image)
928 {
929   int         status, retcode=MS_SUCCESS;
930   int         drawmode=MS_DRAWMODE_FEATURES;
931   char        annotate=MS_TRUE;
932   shapeObj    shape;
933   shapeObj    savedShape;
934   rectObj     searchrect;
935   char        cache=MS_FALSE;
936   int         maxnumstyles=1;
937   featureListNodeObjPtr shpcache=NULL, current=NULL;
938   int nclasses = 0;
939   int *classgroup = NULL;
940   double minfeaturesize = -1;
941   int maxfeatures=-1;
942   int featuresdrawn=0;
943 
944   if (image)
945     maxfeatures=msLayerGetMaxFeaturesToDraw(layer, image->format);
946 
947   /* TODO TBT: draw as raster layer in vector renderers */
948 
949   annotate = msEvalContext(map, layer, layer->labelrequires);
950   if(map->scaledenom > 0) {
951     if((layer->labelmaxscaledenom != -1) && (map->scaledenom >= layer->labelmaxscaledenom)) annotate = MS_FALSE;
952     if((layer->labelminscaledenom != -1) && (map->scaledenom < layer->labelminscaledenom)) annotate = MS_FALSE;
953   }
954 
955   /* open this layer */
956   status = msLayerOpen(layer);
957   if(status != MS_SUCCESS) return MS_FAILURE;
958 
959   /* build item list. STYLEITEM javascript needs the shape attributes */
960   if (layer->styleitem && (strncasecmp(layer->styleitem, "javascript://", 13) == 0)) {
961     status = msLayerWhichItems(layer, MS_TRUE, NULL);
962   } else {
963     status = msLayerWhichItems(layer, MS_FALSE, NULL);
964   }
965 
966   if(status != MS_SUCCESS) {
967     msLayerClose(layer);
968     return MS_FAILURE;
969   }
970 
971   /* identify target shapes */
972   if(layer->transform == MS_TRUE) {
973     searchrect = map->extent;
974 
975     if((map->projection.numargs > 0) && (layer->projection.numargs > 0)) {
976       int bDone = MS_FALSE;
977 
978       if( layer->connectiontype == MS_UVRASTER )
979       {
980           /* Nasty hack to make msUVRASTERLayerWhichShapes() aware that the */
981           /* original area of interest is (map->extent, map->projection)... */
982           /* Useful when dealin with UVRASTER that extend beyond 180 deg */
983           msUVRASTERLayerUseMapExtentAndProjectionForNextWhichShapes( layer, map );
984 
985           searchrect = msUVRASTERGetSearchRect( layer, map );
986           bDone = MS_TRUE;
987       }
988 
989       if( !bDone )
990         msProjectRect(&map->projection, &layer->projection, &searchrect); /* project the searchrect to source coords */
991     }
992 
993   } else {
994     searchrect.minx = searchrect.miny = 0;
995     searchrect.maxx = map->width-1;
996     searchrect.maxy = map->height-1;
997   }
998 
999   status = msLayerWhichShapes(layer, searchrect, MS_FALSE);
1000 
1001   if( layer->connectiontype == MS_UVRASTER )
1002   {
1003     msUVRASTERLayerUseMapExtentAndProjectionForNextWhichShapes( layer, NULL );
1004   }
1005 
1006   if(status == MS_DONE) { /* no overlap */
1007     msLayerClose(layer);
1008     return MS_SUCCESS;
1009   } else if(status != MS_SUCCESS) {
1010     msLayerClose(layer);
1011     return MS_FAILURE;
1012   }
1013 
1014   nclasses = 0;
1015   classgroup = NULL;
1016   if(layer->classgroup && layer->numclasses > 0)
1017     classgroup = msAllocateValidClassGroups(layer, &nclasses);
1018 
1019   if(layer->minfeaturesize > 0)
1020     minfeaturesize = Pix2LayerGeoref(map, layer, layer->minfeaturesize);
1021 
1022   // Select how to render classes
1023   //    MS_FIRST_MATCHING_CLASS: Default and historic MapServer behavior
1024   //    MS_ALL_MATCHING_CLASSES: SLD behavior
1025   int ref_rendermode;
1026   char * rendermodestr = msLayerGetProcessingKey(layer, "RENDERMODE");
1027   if (layer->rendermode == MS_ALL_MATCHING_CLASSES)
1028   {
1029     // SLD takes precedence
1030     ref_rendermode = MS_ALL_MATCHING_CLASSES;
1031   }
1032   else if (!rendermodestr)
1033   {
1034     // Default Mapfile
1035     ref_rendermode = MS_FIRST_MATCHING_CLASS;
1036   }
1037   else if (!strcmp(rendermodestr,"FIRST_MATCHING_CLASS"))
1038   {
1039     // Explicit default Mapfile
1040     ref_rendermode = MS_FIRST_MATCHING_CLASS;
1041   }
1042   else if (!strcmp(rendermodestr,"ALL_MATCHING_CLASSES"))
1043   {
1044     // SLD-like Mapfile
1045     ref_rendermode = MS_ALL_MATCHING_CLASSES;
1046   }
1047   else
1048   {
1049     msLayerClose(layer);
1050     msSetError(MS_MISCERR,
1051     "Unknown RENDERMODE: %s, should be one of: FIRST_MATCHING_CLASS, ALL_MATCHING_CLASSES.",
1052     "msDrawVectorLayer()",
1053     rendermodestr);
1054     return MS_FAILURE;
1055   }
1056 
1057   /* step through the target shapes and their classes */
1058   msInitShape(&shape);
1059   int classindex = -1;
1060   int classcount = 0;
1061   for (;;) {
1062     int rendermode;
1063     if (classindex == -1) {
1064       msFreeShape(&shape);
1065       status = msLayerNextShape(layer, &shape);
1066       if (status != MS_SUCCESS) {
1067         break;
1068       }
1069 
1070       /* Check if the shape size is ok to be drawn */
1071       if((shape.type == MS_SHAPE_LINE || shape.type == MS_SHAPE_POLYGON) && (minfeaturesize > 0) && (msShapeCheckSize(&shape, minfeaturesize) == MS_FALSE)) {
1072         if(layer->debug >= MS_DEBUGLEVEL_V)
1073           msDebug("msDrawVectorLayer(): Skipping shape (%ld) because LAYER::MINFEATURESIZE is bigger than shape size\n", shape.index);
1074         continue;
1075       }
1076       classcount = 0;
1077     }
1078 
1079     classindex = msShapeGetNextClass(classindex, layer, map, &shape, classgroup, nclasses);
1080     if((classindex == -1) || (layer->class[classindex]->status == MS_OFF)) {
1081       continue;
1082     }
1083     shape.classindex = classindex;
1084 
1085     // When only one class is applicable, rendering mode is forced to its default,
1086     // i.e. only the first applicable class is actually applied. As a consequence,
1087     // cache can be enabled when relevant.
1088     classcount++;
1089     rendermode = ref_rendermode;
1090     if ((classcount == 1) && (msShapeGetNextClass(classindex, layer, map, &shape, classgroup, nclasses) == -1))
1091     {
1092       rendermode = MS_FIRST_MATCHING_CLASS;
1093     }
1094 
1095     if (rendermode == MS_FIRST_MATCHING_CLASS)
1096     {
1097       classindex = -1;
1098     }
1099 
1100     if(maxfeatures >=0 && featuresdrawn >= maxfeatures) {
1101       status = MS_DONE;
1102       break;
1103     }
1104     featuresdrawn++;
1105 
1106     cache = MS_FALSE;
1107     if(layer->type == MS_LAYER_LINE && (layer->class[shape.classindex]->numstyles > 1 || (layer->class[shape.classindex]->numstyles == 1 && layer->class[shape.classindex]->styles[0]->outlinewidth > 0))) {
1108       int i;
1109       cache = MS_TRUE; /* only line layers with multiple styles need be cached (I don't think POLYLINE layers need caching - SDL) */
1110 
1111       /* we can't handle caching with attribute binding other than for the first style (#3976) */
1112       for(i=1; i<layer->class[shape.classindex]->numstyles; i++) {
1113         if(layer->class[shape.classindex]->styles[i]->numbindings > 0) cache = MS_FALSE;
1114       }
1115     }
1116 
1117     /* With 'STYLEITEM AUTO', we will have the datasource fill the class' */
1118     /* style parameters for this shape. */
1119     if(layer->styleitem) {
1120       if(strcasecmp(layer->styleitem, "AUTO") == 0) {
1121         if(msLayerGetAutoStyle(map, layer, layer->class[shape.classindex], &shape) != MS_SUCCESS) {
1122           retcode = MS_FAILURE;
1123           break;
1124         }
1125       } else {
1126         /* Generic feature style handling as per RFC-61 */
1127         if(msLayerGetFeatureStyle(map, layer, layer->class[shape.classindex], &shape) != MS_SUCCESS) {
1128           retcode = MS_FAILURE;
1129           break;
1130         }
1131       }
1132 
1133       /* __TODO__ For now, we can't cache features with 'AUTO' style */
1134       cache = MS_FALSE;
1135     }
1136 
1137     if (rendermode == MS_ALL_MATCHING_CLASSES)
1138     {
1139       // Cache is designed to handle only one class. Therefore it is
1140       // disabled when using SLD "painters model" rendering mode.
1141       cache = MS_FALSE;
1142     }
1143 
1144     /* RFC77 TODO: check return value, may need a more sophisticated if-then test. */
1145     if(annotate && layer->class[shape.classindex]->numlabels > 0) {
1146       drawmode |= MS_DRAWMODE_LABELS;
1147       if (msLayerGetProcessingKey(layer, "LABEL_NO_CLIP")) {
1148         drawmode |= MS_DRAWMODE_UNCLIPPEDLABELS;
1149       }
1150     }
1151 
1152     if (layer->type == MS_LAYER_LINE && msLayerGetProcessingKey(layer, "POLYLINE_NO_CLIP")) {
1153       drawmode |= MS_DRAWMODE_UNCLIPPEDLINES;
1154     }
1155 
1156     if (rendermode == MS_ALL_MATCHING_CLASSES)
1157     {
1158       // In SLD "painters model" rendering mode, all applicable classes are actually applied.
1159       // Coordinates stored in the shape must keep their original values for
1160       // the shape to be drawn multiple times.
1161       // Here the original shape is saved.
1162       msInitShape(&savedShape);
1163       msCopyShape(&shape, &savedShape);
1164     }
1165 
1166     if (cache) {
1167       styleObj *pStyle = layer->class[shape.classindex]->styles[0];
1168       if (pStyle->outlinewidth > 0) {
1169         /*
1170          * RFC 49 implementation
1171          * if an outlinewidth is used:
1172          *  - augment the style's width to account for the outline width
1173          *  - swap the style color and outlinecolor
1174          *  - draw the shape (the outline) in the first pass of the
1175          *    caching mechanism
1176          */
1177 	msOutlineRenderingPrepareStyle(pStyle, map, layer, image);
1178       }
1179       status = msDrawShape(map, layer, &shape, image, 0, drawmode|MS_DRAWMODE_SINGLESTYLE); /* draw a single style */
1180       if (pStyle->outlinewidth > 0) {
1181         /*
1182          * RFC 49 implementation: switch back the styleobj to its
1183          * original state, so the line fill will be drawn in the
1184          * second pass of the caching mechanism
1185          */
1186 	msOutlineRenderingRestoreStyle(pStyle, map, layer, image);
1187       }
1188     }
1189 
1190     else
1191       status = msDrawShape(map, layer, &shape, image, -1, drawmode); /* all styles  */
1192 
1193     if (rendermode == MS_ALL_MATCHING_CLASSES)
1194     {
1195       // In SLD "painters model" rendering mode, all applicable classes are actually applied.
1196       // Coordinates stored in the shape must keep their original values for
1197       // the shape to be drawn multiple times.
1198       // Here the original shape is restored.
1199       msFreeShape(&shape);
1200       msCopyShape(&savedShape, &shape);
1201       msFreeShape(&savedShape);
1202     }
1203 
1204     if(status != MS_SUCCESS) {
1205       retcode = MS_FAILURE;
1206       break;
1207     }
1208 
1209     if(shape.numlines == 0) { /* once clipped the shape didn't need to be drawn */
1210       continue;
1211     }
1212 
1213     if(cache) {
1214       if(insertFeatureList(&shpcache, &shape) == NULL) {
1215         retcode = MS_FAILURE; /* problem adding to the cache */
1216         break;
1217       }
1218     }
1219 
1220     maxnumstyles = MS_MAX(maxnumstyles, layer->class[shape.classindex]->numstyles);
1221 
1222   }
1223   msFreeShape(&shape);
1224 
1225   if (classgroup)
1226     msFree(classgroup);
1227 
1228   if(status != MS_DONE || retcode == MS_FAILURE) {
1229     msLayerClose(layer);
1230     if(shpcache) {
1231       freeFeatureList(shpcache);
1232       shpcache = NULL;
1233     }
1234     return MS_FAILURE;
1235   }
1236 
1237   if(shpcache && MS_DRAW_FEATURES(drawmode)) {
1238     int s;
1239     for(s=0; s<maxnumstyles; s++) {
1240       for(current=shpcache; current; current=current->next) {
1241         if(layer->class[current->shape.classindex]->numstyles > s) {
1242           styleObj *pStyle = layer->class[current->shape.classindex]->styles[s];
1243           if(pStyle->_geomtransform.type != MS_GEOMTRANSFORM_NONE)
1244             continue; /*skip this as it has already been rendered*/
1245           if(map->scaledenom > 0) {
1246             if((pStyle->maxscaledenom != -1) && (map->scaledenom >= pStyle->maxscaledenom))
1247               continue;
1248             if((pStyle->minscaledenom != -1) && (map->scaledenom < pStyle->minscaledenom))
1249               continue;
1250           }
1251           if(s==0 && pStyle->outlinewidth>0 && MS_VALID_COLOR(pStyle->color)) {
1252             if(UNLIKELY(MS_FAILURE == msDrawLineSymbol(map, image, &current->shape, pStyle, layer->scalefactor))) {
1253               return MS_FAILURE;
1254             }
1255           } else if(s>0) {
1256             if (pStyle->outlinewidth > 0 && MS_VALID_COLOR(pStyle->outlinecolor)) {
1257               /*
1258                * RFC 49 implementation
1259                * if an outlinewidth is used:
1260                *  - augment the style's width to account for the outline width
1261                *  - swap the style color and outlinecolor
1262                *  - draw the shape (the outline) in the first pass of the
1263                *    caching mechanism
1264                */
1265 	      msOutlineRenderingPrepareStyle(pStyle, map, layer, image);
1266               if(UNLIKELY(MS_FAILURE == msDrawLineSymbol(map, image, &current->shape, pStyle, layer->scalefactor))) {
1267                 return MS_FAILURE;
1268               }
1269               /*
1270                * RFC 49 implementation: switch back the styleobj to its
1271                * original state, so the line fill will be drawn in the
1272                * second pass of the caching mechanism
1273                */
1274 	      msOutlineRenderingRestoreStyle(pStyle, map, layer, image);
1275             }
1276             /* draw a valid line, i.e. one with a color defined or of type pixmap*/
1277             if(MS_VALID_COLOR(pStyle->color) ||
1278                     (
1279                       pStyle->symbol<map->symbolset.numsymbols &&
1280                       (
1281                         map->symbolset.symbol[pStyle->symbol]->type == MS_SYMBOL_PIXMAP ||
1282                         map->symbolset.symbol[pStyle->symbol]->type == MS_SYMBOL_SVG
1283                       )
1284                     )
1285               ) {
1286               if(UNLIKELY(MS_FAILURE == msDrawLineSymbol(map, image, &current->shape, pStyle, layer->scalefactor)))
1287                 return MS_FAILURE;
1288             }
1289           }
1290         }
1291       }
1292     }
1293 
1294     freeFeatureList(shpcache);
1295     shpcache = NULL;
1296   }
1297 
1298   msLayerClose(layer);
1299   return MS_SUCCESS;
1300 
1301 }
1302 
1303 /*
1304 ** Function to draw a layer IF it already has a result cache associated with it. Called by msDrawMap and via MapScript.
1305 */
msDrawQueryLayer(mapObj * map,layerObj * layer,imageObj * image)1306 int msDrawQueryLayer(mapObj *map, layerObj *layer, imageObj *image)
1307 {
1308   int i, status;
1309   char annotate=MS_TRUE, cache=MS_FALSE;
1310   int drawmode = MS_DRAWMODE_FEATURES;
1311   shapeObj shape;
1312   int maxnumstyles=1;
1313 
1314   featureListNodeObjPtr shpcache=NULL, current=NULL;
1315 
1316   colorObj *colorbuffer = NULL;
1317   int *mindistancebuffer = NULL;
1318 
1319   if(!layer->resultcache) return(msDrawLayer(map, layer, image));
1320 
1321   if(!msLayerIsVisible(map, layer)) return(MS_SUCCESS); /* not an error, just nothing to do */
1322 
1323   /* conditions may have changed since this layer last drawn, so reset
1324      layer->project to recheck projection needs (Bug #673) */
1325   layer->project = msProjectionsDiffer(&(layer->projection),&(map->projection));
1326 
1327   /* set annotation status */
1328   annotate = msEvalContext(map, layer, layer->labelrequires);
1329   if(map->scaledenom > 0) {
1330     if((layer->labelmaxscaledenom != -1) && (map->scaledenom >= layer->labelmaxscaledenom)) annotate = MS_FALSE;
1331     if((layer->labelminscaledenom != -1) && (map->scaledenom < layer->labelminscaledenom)) annotate = MS_FALSE;
1332   }
1333 
1334   /*
1335   ** Certain query map styles require us to render all features only (MS_NORMAL) or first (MS_HILITE). With
1336   ** single-pass queries we have to make a copy of the layer and work from it instead.
1337   */
1338   if(map->querymap.style == MS_NORMAL || map->querymap.style == MS_HILITE) {
1339     layerObj tmp_layer;
1340 
1341     if(initLayer(&tmp_layer, map) == -1)
1342       return(MS_FAILURE);
1343 
1344     if (msCopyLayer(&tmp_layer, layer) != MS_SUCCESS)
1345       return(MS_FAILURE);
1346 
1347     /* disable the connection pool for this layer */
1348     msLayerSetProcessingKey(&tmp_layer, "CLOSE_CONNECTION", "ALWAYS");
1349 
1350     status = msDrawLayer(map, &tmp_layer, image);
1351 
1352     freeLayer(&tmp_layer);
1353 
1354     if(map->querymap.style == MS_NORMAL || status != MS_SUCCESS) return(status);
1355   }
1356 
1357   /* if MS_HILITE, alter the one style (always at least 1 style), and set a MINDISTANCE for the labelObj to avoid duplicates */
1358   if(map->querymap.style == MS_HILITE) {
1359 
1360     drawmode |= MS_DRAWMODE_QUERY;
1361 
1362     if (layer->numclasses > 0) {
1363       colorbuffer = (colorObj*)msSmallMalloc(layer->numclasses*sizeof(colorObj));
1364       mindistancebuffer = (int*)msSmallMalloc(layer->numclasses*sizeof(int));
1365     }
1366 
1367     for(i=0; i<layer->numclasses; i++) {
1368       if(layer->type == MS_LAYER_POLYGON && layer->class[i]->numstyles > 0) { /* alter BOTTOM style since that's almost always the fill */
1369         if (layer->class[i]->styles == NULL) {
1370           msSetError(MS_MISCERR, "Don't know how to draw class %s of layer %s without a style definition.", "msDrawQueryLayer()", layer->class[i]->name, layer->name);
1371           msFree(colorbuffer);
1372           msFree(mindistancebuffer);
1373           return(MS_FAILURE);
1374         }
1375         if(MS_VALID_COLOR(layer->class[i]->styles[0]->color)) {
1376           colorbuffer[i] = layer->class[i]->styles[0]->color; /* save the color from the BOTTOM style */
1377           layer->class[i]->styles[0]->color = map->querymap.color;
1378         } else if(MS_VALID_COLOR(layer->class[i]->styles[0]->outlinecolor)) {
1379           colorbuffer[i] = layer->class[i]->styles[0]->outlinecolor; /* if no color, save the outlinecolor from the BOTTOM style */
1380           layer->class[i]->styles[0]->outlinecolor = map->querymap.color;
1381         }
1382       } else if (layer->type == MS_LAYER_LINE && layer->class[i]->numstyles > 0 && layer->class[i]->styles[0]->outlinewidth > 0) { /* alter BOTTOM style for lines with outlines */
1383 	if(MS_VALID_COLOR(layer->class[i]->styles[0]->color)) {
1384           colorbuffer[i] = layer->class[i]->styles[0]->color; /* save the color from the BOTTOM style */
1385           layer->class[i]->styles[0]->color = map->querymap.color;
1386         } /* else ??? */
1387       } else if (layer->class[i]->numstyles > 0) {
1388         if(MS_VALID_COLOR(layer->class[i]->styles[layer->class[i]->numstyles-1]->color)) {
1389           colorbuffer[i] = layer->class[i]->styles[layer->class[i]->numstyles-1]->color; /* save the color from the TOP style */
1390           layer->class[i]->styles[layer->class[i]->numstyles-1]->color = map->querymap.color;
1391         } else if(MS_VALID_COLOR(layer->class[i]->styles[layer->class[i]->numstyles-1]->outlinecolor)) {
1392           colorbuffer[i] = layer->class[i]->styles[layer->class[i]->numstyles-1]->outlinecolor; /* if no color, save the outlinecolor from the TOP style */
1393           layer->class[i]->styles[layer->class[i]->numstyles-1]->outlinecolor = map->querymap.color;
1394         }
1395       } else if (layer->class[i]->numlabels > 0) {
1396           colorbuffer[i] = layer->class[i]->labels[0]->color;
1397           layer->class[i]->labels[0]->color = map->querymap.color;
1398       } /* else ??? */
1399 
1400       mindistancebuffer[i] = -1; /* RFC77 TODO: only using the first label, is that cool? */
1401       if(layer->class[i]->numlabels > 0) {
1402         mindistancebuffer[i] = layer->class[i]->labels[0]->mindistance;
1403         layer->class[i]->labels[0]->mindistance = MS_MAX(0, layer->class[i]->labels[0]->mindistance);
1404       }
1405     }
1406   }
1407 
1408   /*
1409   ** Layer was opened as part of the query process, msLayerWhichItems() has also been run, shapes have been classified - start processing!
1410   */
1411 
1412   msInitShape(&shape);
1413 
1414   for(i=0; i<layer->resultcache->numresults; i++) {
1415     status = msLayerGetShape(layer, &shape, &(layer->resultcache->results[i]));
1416     if(status != MS_SUCCESS) {
1417       msFree(colorbuffer);
1418       msFree(mindistancebuffer);
1419       return(MS_FAILURE);
1420     }
1421 
1422     shape.classindex = layer->resultcache->results[i].classindex;
1423     /* classindex may be -1 here if there was a template at the top level
1424      * in this layer and the current shape selected even if it didn't
1425      * match any class
1426      *
1427      * FrankW: classindex is also sometimes 0 even if there are no classes, so
1428      * we are careful not to use out of range class values as an index.
1429      */
1430     if(shape.classindex==-1
1431         || shape.classindex >= layer->numclasses
1432         || layer->class[shape.classindex]->status == MS_OFF) {
1433       msFreeShape(&shape);
1434       continue;
1435     }
1436 
1437     cache = MS_FALSE;
1438     if(layer->type == MS_LAYER_LINE && (layer->class[shape.classindex]->numstyles > 1 || (layer->class[shape.classindex]->numstyles == 1 && layer->class[shape.classindex]->styles[0]->outlinewidth > 0))) {
1439       int i;
1440       cache = MS_TRUE; /* only line layers with multiple styles need be cached (I don't think POLYLINE layers need caching - SDL) */
1441 
1442       /* we can't handle caching with attribute binding other than for the first style (#3976) */
1443       for(i=1; i<layer->class[shape.classindex]->numstyles; i++) {
1444         if(layer->class[shape.classindex]->styles[i]->numbindings > 0) cache = MS_FALSE;
1445       }
1446     }
1447 
1448     if(annotate && layer->class[shape.classindex]->numlabels > 0) {
1449       drawmode |= MS_DRAWMODE_LABELS;
1450     }
1451 
1452     if(cache) {
1453       styleObj *pStyle = layer->class[shape.classindex]->styles[0];
1454       if (pStyle->outlinewidth > 0) msOutlineRenderingPrepareStyle(pStyle, map, layer, image);
1455       status = msDrawShape(map, layer, &shape, image, 0, drawmode|MS_DRAWMODE_SINGLESTYLE); /* draw only the first style */
1456       if (pStyle->outlinewidth > 0) msOutlineRenderingRestoreStyle(pStyle, map, layer, image);
1457     } else {
1458       status = msDrawShape(map, layer, &shape, image, -1, drawmode); /* all styles  */
1459     }
1460     if(status != MS_SUCCESS) {
1461       msLayerClose(layer);
1462       msFree(colorbuffer);
1463       msFree(mindistancebuffer);
1464       return(MS_FAILURE);
1465     }
1466 
1467     if(shape.numlines == 0) { /* once clipped the shape didn't need to be drawn */
1468       msFreeShape(&shape);
1469       continue;
1470     }
1471 
1472     if(cache) {
1473       if(insertFeatureList(&shpcache, &shape) == NULL) {
1474         msFree(colorbuffer);
1475         msFree(mindistancebuffer);
1476         return(MS_FAILURE); /* problem adding to the cache */
1477       }
1478     }
1479 
1480     maxnumstyles = MS_MAX(maxnumstyles, layer->class[shape.classindex]->numstyles);
1481     msFreeShape(&shape);
1482   }
1483 
1484   if(shpcache) {
1485     int s;
1486     for(s=0; s<maxnumstyles; s++) {
1487       for(current=shpcache; current; current=current->next) {
1488         if(layer->class[current->shape.classindex]->numstyles > s) {
1489           styleObj *pStyle = layer->class[current->shape.classindex]->styles[s];
1490           if(pStyle->_geomtransform.type != MS_GEOMTRANSFORM_NONE)
1491             continue; /* skip this as it has already been rendered */
1492           if(map->scaledenom > 0) {
1493             if((pStyle->maxscaledenom != -1) && (map->scaledenom >= pStyle->maxscaledenom))
1494               continue;
1495             if((pStyle->minscaledenom != -1) && (map->scaledenom < pStyle->minscaledenom))
1496               continue;
1497           }
1498           if(s==0 && pStyle->outlinewidth>0 && MS_VALID_COLOR(pStyle->color)) {
1499             if(UNLIKELY(MS_FAILURE == msDrawLineSymbol(map, image, &current->shape, pStyle, layer->scalefactor))) {
1500               return MS_FAILURE;
1501             }
1502           } else if(s>0) {
1503             if (pStyle->outlinewidth > 0 && MS_VALID_COLOR(pStyle->outlinecolor)) {
1504               msOutlineRenderingPrepareStyle(pStyle, map, layer, image);
1505               if(UNLIKELY(MS_FAILURE == msDrawLineSymbol(map, image, &current->shape, pStyle, layer->scalefactor))) {
1506                 return MS_FAILURE;
1507               }
1508               msOutlineRenderingRestoreStyle(pStyle, map, layer, image);
1509             }
1510             /* draw a valid line, i.e. one with a color defined or of type pixmap */
1511             if(MS_VALID_COLOR(pStyle->color) || (pStyle->symbol<map->symbolset.numsymbols && (map->symbolset.symbol[pStyle->symbol]->type == MS_SYMBOL_PIXMAP || map->symbolset.symbol[pStyle->symbol]->type == MS_SYMBOL_SVG))) {
1512               if(UNLIKELY(MS_FAILURE == msDrawLineSymbol(map, image, &current->shape, pStyle, layer->scalefactor)))
1513                 return MS_FAILURE;
1514             }
1515           }
1516         }
1517       }
1518     }
1519 
1520     freeFeatureList(shpcache);
1521     shpcache = NULL;
1522   }
1523 
1524   /* if MS_HILITE, restore color and mindistance values */
1525   if(map->querymap.style == MS_HILITE) {
1526     for(i=0; i<layer->numclasses; i++) {
1527       if(layer->type == MS_LAYER_POLYGON && layer->class[i]->numstyles > 0) {
1528         if(MS_VALID_COLOR(layer->class[i]->styles[0]->color))
1529           layer->class[i]->styles[0]->color = colorbuffer[i];
1530         else if(MS_VALID_COLOR(layer->class[i]->styles[0]->outlinecolor))
1531           layer->class[i]->styles[0]->outlinecolor = colorbuffer[i]; /* if no color, restore outlinecolor for the BOTTOM style */
1532       } else if (layer->type == MS_LAYER_LINE && layer->class[i]->numstyles > 0 && layer->class[i]->styles[0]->outlinewidth > 0) {
1533         if(MS_VALID_COLOR(layer->class[i]->styles[0]->color))
1534 	  layer->class[i]->styles[0]->color = colorbuffer[i];
1535       } else if (layer->class[i]->numstyles > 0) {
1536         if(MS_VALID_COLOR(layer->class[i]->styles[layer->class[i]->numstyles-1]->color))
1537           layer->class[i]->styles[layer->class[i]->numstyles-1]->color = colorbuffer[i];
1538         else if(MS_VALID_COLOR(layer->class[i]->styles[layer->class[i]->numstyles-1]->outlinecolor))
1539           layer->class[i]->styles[layer->class[i]->numstyles-1]->outlinecolor = colorbuffer[i]; /* if no color, restore outlinecolor for the TOP style */
1540       } else if (layer->class[i]->numlabels > 0) {
1541         if(MS_VALID_COLOR(layer->class[i]->labels[0]->color))
1542           layer->class[i]->labels[0]->color = colorbuffer[i];
1543       }
1544 
1545       if(layer->class[i]->numlabels > 0)
1546         layer->class[i]->labels[0]->mindistance = mindistancebuffer[i]; /* RFC77 TODO: again, only using the first label, is that cool? */
1547     }
1548 
1549     msFree(colorbuffer);
1550     msFree(mindistancebuffer);
1551   }
1552 
1553   return(MS_SUCCESS);
1554 }
1555 
1556 /**
1557  * msDrawRasterLayerPlugin()
1558  */
1559 
1560 static int
msDrawRasterLayerPlugin(mapObj * map,layerObj * layer,imageObj * image)1561 msDrawRasterLayerPlugin( mapObj *map, layerObj *layer, imageObj *image)
1562 
1563 {
1564   rendererVTableObj *renderer = MS_IMAGE_RENDERER(image);
1565   rasterBufferObj  *rb = msSmallCalloc(1,sizeof(rasterBufferObj));
1566   int ret;
1567   if( renderer->supports_pixel_buffer ) {
1568     if (MS_SUCCESS != renderer->getRasterBufferHandle( image, rb )) {
1569       msSetError(MS_MISCERR,"renderer failed to extract raster buffer","msDrawRasterLayer()");
1570       return MS_FAILURE;
1571     }
1572     ret = msDrawRasterLayerLow( map, layer, image, rb );
1573   } else {
1574     if (MS_SUCCESS != renderer->initializeRasterBuffer( rb, image->width, image->height, MS_IMAGEMODE_RGBA )) {
1575       msSetError(MS_MISCERR,"renderer failed to create raster buffer","msDrawRasterLayer()");
1576       return MS_FAILURE;
1577     }
1578 
1579     ret = msDrawRasterLayerLow( map, layer, image, rb );
1580 
1581     if( ret == 0 ) {
1582       ret = renderer->mergeRasterBuffer( image, rb, 1.0, 0, 0, 0, 0, rb->width, rb->height );
1583     }
1584 
1585     msFreeRasterBuffer(rb);
1586   }
1587 #define RB_GET_R(rb,x,y) *((rb)->data.rgba.r + (x) * (rb)->data.rgba.pixel_step + (y) * (rb)->data.rgba.row_step)
1588 #define RB_GET_G(rb,x,y) *((rb)->data.rgba.g + (x) * (rb)->data.rgba.pixel_step + (y) * (rb)->data.rgba.row_step)
1589 #define RB_GET_B(rb,x,y) *((rb)->data.rgba.b + (x) * (rb)->data.rgba.pixel_step + (y) * (rb)->data.rgba.row_step)
1590 #define RB_GET_A(rb,x,y) *((rb)->data.rgba.a + (x) * (rb)->data.rgba.pixel_step + (y) * (rb)->data.rgba.row_step)
1591 
1592   free(rb);
1593 
1594   return ret;
1595 }
1596 
1597 /**
1598  * Generic function to render raster layers.
1599  */
msDrawRasterLayer(mapObj * map,layerObj * layer,imageObj * image)1600 int msDrawRasterLayer(mapObj *map, layerObj *layer, imageObj *image)
1601 {
1602 
1603   int rv = MS_FAILURE;
1604   if (!image || !map || !layer) {
1605     return rv;
1606   }
1607 
1608   /* RFC-86 Scale dependant token replacements*/
1609   rv = msLayerApplyScaletokens(layer,(layer->map)?layer->map->scaledenom:-1);
1610   if (rv != MS_SUCCESS) return rv;
1611   if( MS_RENDERER_PLUGIN(image->format) )
1612     rv = msDrawRasterLayerPlugin(map, layer, image);
1613   else if( MS_RENDERER_RAWDATA(image->format) )
1614     rv = msDrawRasterLayerLow(map, layer, image, NULL);
1615   msLayerRestoreFromScaletokens(layer);
1616   return rv;
1617 }
1618 
1619 /**
1620  * msDrawWMSLayer()
1621  *
1622  * Draw a single WMS layer.
1623  * Multiple WMS layers in a map are preloaded and then drawn using
1624  * msDrawWMSLayerLow()
1625  */
1626 
1627 #ifdef USE_WMS_LYR
msDrawWMSLayer(mapObj * map,layerObj * layer,imageObj * image)1628 int msDrawWMSLayer(mapObj *map, layerObj *layer, imageObj *image)
1629 {
1630   int nStatus = MS_FAILURE;
1631 
1632   if (image && map && layer) {
1633     /* ------------------------------------------------------------------
1634      * Start by downloading the layer
1635      * ------------------------------------------------------------------ */
1636     httpRequestObj asReqInfo[2];
1637     int numReq = 0;
1638 
1639     msHTTPInitRequestObj(asReqInfo, 2);
1640 
1641     if ( msPrepareWMSLayerRequest(1, map, layer, 1,
1642                                   0, NULL, 0, 0, 0, NULL,
1643                                   asReqInfo, &numReq) == MS_FAILURE  ||
1644          msOWSExecuteRequests(asReqInfo, numReq, map, MS_TRUE) == MS_FAILURE ) {
1645       return MS_FAILURE;
1646     }
1647 
1648     /* ------------------------------------------------------------------
1649      * Then draw layer based on output format
1650      * ------------------------------------------------------------------ */
1651     if( MS_RENDERER_PLUGIN(image->format) )
1652       nStatus = msDrawWMSLayerLow(1, asReqInfo, numReq,
1653                                   map, layer, image) ;
1654     else if( MS_RENDERER_RAWDATA(image->format) )
1655       nStatus = msDrawWMSLayerLow(1, asReqInfo, numReq,
1656                                   map, layer, image) ;
1657 
1658     else {
1659       msSetError(MS_WMSCONNERR,
1660                  "Output format '%s' doesn't support WMS layers.",
1661                  "msDrawWMSLayer()", image->format->name);
1662       nStatus = MS_SUCCESS; /* Should we fail if output doesn't support WMS? */
1663     }
1664     /* Cleanup */
1665     msHTTPFreeRequestObj(asReqInfo, numReq);
1666   }
1667 
1668   return nStatus;
1669 }
1670 #endif
1671 
circleLayerDrawShape(mapObj * map,imageObj * image,layerObj * layer,shapeObj * shape)1672 int circleLayerDrawShape(mapObj *map, imageObj *image, layerObj *layer, shapeObj *shape)
1673 {
1674   pointObj center;
1675   double r;
1676   int s;
1677   int c = shape->classindex;
1678 
1679   if (shape->numlines != 1) return (MS_SUCCESS); /* invalid shape */
1680   if (shape->line[0].numpoints != 2) return (MS_SUCCESS); /* invalid shape */
1681 
1682   center.x = (shape->line[0].point[0].x + shape->line[0].point[1].x) / 2.0;
1683   center.y = (shape->line[0].point[0].y + shape->line[0].point[1].y) / 2.0;
1684   r = MS_ABS(center.x - shape->line[0].point[0].x);
1685   if (r == 0)
1686     r = MS_ABS(center.y - shape->line[0].point[0].y);
1687   if (r == 0)
1688     return (MS_SUCCESS);
1689 
1690   if (layer->transform == MS_TRUE) {
1691 
1692     if (layer->project)
1693       msProjectPoint(&layer->projection, &map->projection, &center);
1694 
1695     center.x = MS_MAP2IMAGE_X(center.x, map->extent.minx, map->cellsize);
1696     center.y = MS_MAP2IMAGE_Y(center.y, map->extent.maxy, map->cellsize);
1697     r /= map->cellsize;
1698   } else
1699     msOffsetPointRelativeTo(&center, layer);
1700 
1701   for (s = 0; s < layer->class[c]->numstyles; s++) {
1702     if (msScaleInBounds(map->scaledenom,
1703                         layer->class[c]->styles[s]->minscaledenom,
1704                         layer->class[c]->styles[s]->maxscaledenom))
1705       if(UNLIKELY(MS_FAILURE == msCircleDrawShadeSymbol(map, image, &center, r,
1706                               layer->class[c]->styles[s], layer->scalefactor))) {
1707         return MS_FAILURE;
1708       }
1709   }
1710   return MS_SUCCESS;
1711   /* TODO: need to handle circle annotation */
1712 }
1713 
1714 static
pointLayerDrawShape(mapObj * map,imageObj * image,layerObj * layer,shapeObj * shape,int drawmode)1715 int pointLayerDrawShape(mapObj *map, imageObj *image, layerObj *layer, shapeObj *shape, int drawmode)
1716 {
1717   int l, c = shape->classindex, j, i, s;
1718   pointObj *point;
1719   int ret = MS_FAILURE;
1720 
1721   if (layer->project && layer->transform == MS_TRUE)
1722   {
1723       if( layer->reprojectorLayerToMap == NULL )
1724       {
1725           layer->reprojectorLayerToMap = msProjectCreateReprojector(
1726               &layer->projection, &map->projection);
1727         if( layer->reprojectorLayerToMap == NULL )
1728         {
1729             return MS_FAILURE;
1730         }
1731       }
1732       msProjectShapeEx(layer->reprojectorLayerToMap, shape);
1733   }
1734 
1735   // Only take into account map rotation if the label and style angles are
1736   // non-zero.
1737   if( map->gt.rotation_angle )
1738   {
1739     for (l = 0; l < layer->class[c]->numlabels; l++)
1740     {
1741         if( layer->class[c]->labels[l]->angle != 0 )
1742             layer->class[c]->labels[l]->angle -= map->gt.rotation_angle;
1743     }
1744 
1745     for (s = 0; s < layer->class[c]->numstyles; s++)
1746     {
1747         if( layer->class[c]->styles[s]->angle != 0 )
1748             layer->class[c]->styles[s]->angle -= map->gt.rotation_angle;
1749     }
1750   }
1751 
1752   for (j = 0; j < shape->numlines; j++) {
1753     for (i = 0; i < shape->line[j].numpoints; i++) {
1754       point = &(shape->line[j].point[i]);
1755       if (layer->transform == MS_TRUE) {
1756         if (!msPointInRect(point, &map->extent)) continue; /* next point */
1757         msTransformPoint(point, &map->extent, map->cellsize, image);
1758       } else
1759         msOffsetPointRelativeTo(point, layer);
1760 
1761       if(MS_DRAW_FEATURES(drawmode)) {
1762         for (s = 0; s < layer->class[c]->numstyles; s++) {
1763           if (msScaleInBounds(map->scaledenom,
1764               layer->class[c]->styles[s]->minscaledenom,
1765               layer->class[c]->styles[s]->maxscaledenom))
1766             if(UNLIKELY(MS_FAILURE == msDrawMarkerSymbol(map, image, point, layer->class[c]->styles[s], layer->scalefactor))) {
1767               goto end;
1768             }
1769         }
1770       }
1771       if(MS_DRAW_LABELS(drawmode)) {
1772         if (layer->labelcache) {
1773           if (msAddLabelGroup(map, image, layer, c, shape, point, -1) != MS_SUCCESS) goto end;
1774         } else {
1775           for (l = 0; l < layer->class[c]->numlabels; l++)
1776             if(msGetLabelStatus(map,layer,shape,layer->class[c]->labels[l]) == MS_ON) {
1777               char *annotext = msShapeGetLabelAnnotation(layer,shape,layer->class[c]->labels[l]);
1778               if(UNLIKELY(MS_FAILURE == msDrawLabel(map, image, *point, annotext, layer->class[c]->labels[l], layer->scalefactor))) {
1779                 goto end;
1780               }
1781             }
1782         }
1783       }
1784     }
1785   }
1786   ret = MS_SUCCESS;
1787 
1788 end:
1789   if( map->gt.rotation_angle )
1790   {
1791     for (l = 0; l < layer->class[c]->numlabels; l++)
1792     {
1793         if( layer->class[c]->labels[l]->angle != 0 )
1794             layer->class[c]->labels[l]->angle += map->gt.rotation_angle;
1795     }
1796 
1797     for (s = 0; s < layer->class[c]->numstyles; s++)
1798     {
1799         if( layer->class[c]->styles[s]->angle != 0 )
1800             layer->class[c]->styles[s]->angle += map->gt.rotation_angle;
1801     }
1802   }
1803 
1804   return ret;
1805 }
1806 
lineLayerDrawShape(mapObj * map,imageObj * image,layerObj * layer,shapeObj * shape,shapeObj * anno_shape,shapeObj * unclipped_shape,int style,int drawmode)1807 int lineLayerDrawShape(mapObj *map, imageObj *image, layerObj *layer, shapeObj *shape,
1808                        shapeObj *anno_shape, shapeObj *unclipped_shape, int style, int drawmode)
1809 {
1810   int c = shape->classindex;
1811   int ret = MS_SUCCESS;
1812   int i, s, l = 0;
1813 
1814   /* RFC48: loop through the styles, and pass off to the type-specific
1815   function if the style has an appropriate type */
1816   if(MS_DRAW_FEATURES(drawmode)) {
1817     for (s = 0; s < layer->class[c]->numstyles; s++) {
1818       if (msScaleInBounds(map->scaledenom,
1819           layer->class[c]->styles[s]->minscaledenom,
1820           layer->class[c]->styles[s]->maxscaledenom)) {
1821         if (layer->class[c]->styles[s]->_geomtransform.type != MS_GEOMTRANSFORM_NONE) {
1822           if(UNLIKELY(MS_FAILURE == msDrawTransformedShape(map, image, unclipped_shape, layer->class[c]->styles[s], layer->scalefactor))) {
1823             return MS_FAILURE;
1824           }
1825         }
1826         else if (!MS_DRAW_SINGLESTYLE(drawmode) || s == style) {
1827           if(UNLIKELY(MS_FAILURE == msDrawLineSymbol(map, image, shape, layer->class[c]->styles[s], layer->scalefactor))) {
1828             return MS_FAILURE;
1829           }
1830         }
1831       }
1832     }
1833   }
1834 
1835   if(MS_DRAW_LABELS(drawmode)) {
1836     for (l = 0; l < layer->class[c]->numlabels; l++) {
1837       labelObj *label = layer->class[c]->labels[l];
1838       textSymbolObj ts;
1839       char *annotext;
1840       if(!msGetLabelStatus(map,layer,shape,label)) {
1841         continue;
1842       }
1843 
1844       annotext = msShapeGetLabelAnnotation(layer,anno_shape,label);
1845       if(!annotext) continue;
1846       initTextSymbol(&ts);
1847       msPopulateTextSymbolForLabelAndString(&ts,label,annotext,layer->scalefactor,image->resolutionfactor, layer->labelcache);
1848 
1849 
1850       if (label->anglemode == MS_FOLLOW) { /* bug #1620 implementation */
1851         struct label_follow_result lfr;
1852 
1853         if (!layer->labelcache) {
1854           msSetError(MS_MISCERR, "Angle mode 'FOLLOW' is supported only with labelcache on", "msDrawShape()");
1855           ret = MS_FAILURE;
1856           goto line_cleanup;
1857         }
1858 
1859         memset(&lfr,0,sizeof(lfr));
1860         msPolylineLabelPath(map, image, anno_shape, &ts, label, &lfr);
1861 
1862         for (i = 0; i < lfr.num_follow_labels; i++) {
1863           if (msAddLabel(map, image, label, layer->index, c, anno_shape, NULL, -1, lfr.follow_labels[i]) != MS_SUCCESS) {
1864             ret = MS_FAILURE;
1865             goto line_cleanup;
1866           }
1867         }
1868         free(lfr.follow_labels);
1869         for(i=0; i<lfr.lar.num_label_points; i++) {
1870           textSymbolObj *ts_auto = msSmallMalloc(sizeof(textSymbolObj));
1871           initTextSymbol(ts_auto);
1872           msCopyTextSymbol(ts_auto,&ts);
1873           ts_auto->rotation = lfr.lar.angles[i];
1874           if (layer->labelcache) {
1875             if (msAddLabel(map, image, label, layer->index, c, anno_shape, &lfr.lar.label_points[i], -1, ts_auto) != MS_SUCCESS) {
1876               ret = MS_FAILURE;
1877               free(lfr.lar.angles);
1878               free(lfr.lar.label_points);
1879               goto line_cleanup;
1880             }
1881           } else {
1882             ret = msDrawTextSymbol(map,image,lfr.lar.label_points[i],ts_auto);
1883             freeTextSymbol(ts_auto);
1884             free(ts_auto); /* TODO RFC98: could we not re-use the original ts instead of duplicating into ts_auto ?
1885                             * we cannot for now, as the rendering code will modify the glyph positions to apply
1886                             * the labelpoint and rotation offsets */
1887             if(UNLIKELY(MS_FAILURE == ret)) goto line_cleanup;
1888           }
1889 
1890         }
1891         free(lfr.lar.angles);
1892         free(lfr.lar.label_points);
1893       } else {
1894         struct label_auto_result lar;
1895         memset(&lar,0,sizeof(struct label_auto_result));
1896         ret = msPolylineLabelPoint(map, anno_shape, &ts, label, &lar, image->resolutionfactor);
1897         if(UNLIKELY(MS_FAILURE == ret)) goto line_cleanup;
1898 
1899         if (label->angle != 0)
1900           label->angle -= map->gt.rotation_angle; /* apply rotation angle */
1901 
1902         for(i=0; i<lar.num_label_points; i++) {
1903           textSymbolObj *ts_auto = msSmallMalloc(sizeof(textSymbolObj));
1904           initTextSymbol(ts_auto);
1905           msCopyTextSymbol(ts_auto,&ts);
1906           ts_auto->rotation = lar.angles[i];
1907           if (layer->labelcache) {
1908             if (msAddLabel(map, image, label, layer->index, c, anno_shape, &lar.label_points[i], -1, ts_auto) != MS_SUCCESS) {
1909               ret = MS_FAILURE;
1910               free(lar.angles);
1911               free(lar.label_points);
1912               freeTextSymbol(ts_auto);
1913               free(ts_auto);
1914               goto line_cleanup;
1915             }
1916           } else {
1917             if(!ts_auto->textpath) {
1918               if(UNLIKELY(MS_FAILURE == msComputeTextPath(map,ts_auto))) {
1919                 ret = MS_FAILURE;
1920                 free(lar.angles);
1921                 free(lar.label_points);
1922                 freeTextSymbol(ts_auto);
1923                 free(ts_auto);
1924                 goto line_cleanup;
1925               }
1926             }
1927             ret = msDrawTextSymbol(map,image,lar.label_points[i],ts_auto);
1928             freeTextSymbol(ts_auto);
1929             free(ts_auto); /* TODO RFC98: could we not re-use the original ts instead of duplicating into ts_auto ?
1930                             * we cannot for now, as the rendering code will modify the glyph positions to apply
1931                             * the labelpoint and rotation offsets */
1932             ts_auto = NULL;
1933             if(UNLIKELY(MS_FAILURE == ret)) goto line_cleanup;
1934           }
1935 
1936         }
1937         free(lar.angles);
1938         free(lar.label_points);
1939       }
1940 
1941 line_cleanup:
1942       /* clean up and reset */
1943       if (ret == MS_FAILURE) {
1944         break; /* from the label looping */
1945       }
1946       freeTextSymbol(&ts);
1947     } /* next label */
1948   }
1949 
1950   return ret;
1951 
1952 }
1953 
polygonLayerDrawShape(mapObj * map,imageObj * image,layerObj * layer,shapeObj * shape,shapeObj * anno_shape,shapeObj * unclipped_shape,int drawmode)1954 int polygonLayerDrawShape(mapObj *map, imageObj *image, layerObj *layer,
1955                           shapeObj *shape, shapeObj *anno_shape, shapeObj *unclipped_shape, int drawmode)
1956 {
1957 
1958   int c = shape->classindex;
1959   pointObj annopnt = {0,0,0,0}; // initialize
1960   int i;
1961 
1962   if(MS_DRAW_FEATURES(drawmode)) {
1963     for (i = 0; i < layer->class[c]->numstyles; i++) {
1964       if (msScaleInBounds(map->scaledenom, layer->class[c]->styles[i]->minscaledenom,
1965                           layer->class[c]->styles[i]->maxscaledenom)) {
1966         if (layer->class[c]->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_NONE) {
1967           if(UNLIKELY(MS_FAILURE == msDrawShadeSymbol(map, image, shape, layer->class[c]->styles[i], layer->scalefactor))) {
1968             return MS_FAILURE;
1969           }
1970         }
1971         else {
1972           if(UNLIKELY(MS_FAILURE == msDrawTransformedShape(map, image, unclipped_shape,
1973                                  layer->class[c]->styles[i], layer->scalefactor))) {
1974             return MS_FAILURE;
1975           }
1976         }
1977       }
1978     }
1979   }
1980 
1981   if(MS_DRAW_LABELS(drawmode)) {
1982     if (layer->class[c]->numlabels > 0) {
1983       double minfeaturesize = layer->class[c]->labels[0]->minfeaturesize * image->resolutionfactor;
1984       if (msPolygonLabelPoint(anno_shape, &annopnt, minfeaturesize) == MS_SUCCESS) {
1985         for (i = 0; i < layer->class[c]->numlabels; i++)
1986           if (layer->class[c]->labels[i]->angle != 0) layer->class[c]->labels[i]->angle -= map->gt.rotation_angle; /* TODO: is this correct ??? */
1987         if (layer->labelcache) {
1988           if (msAddLabelGroup(map, image, layer, c, anno_shape, &annopnt,
1989                               MS_MIN(shape->bounds.maxx - shape->bounds.minx, shape->bounds.maxy - shape->bounds.miny)) != MS_SUCCESS) {
1990             return MS_FAILURE;
1991           }
1992         } else {
1993           for (i = 0; i < layer->class[c]->numlabels; i++)
1994             if(msGetLabelStatus(map,layer,shape,layer->class[c]->labels[i]) == MS_ON) {
1995               char *annotext = msShapeGetLabelAnnotation(layer,shape,layer->class[c]->labels[i]); /*ownership taken by msDrawLabel, no need to free */
1996               if(UNLIKELY(MS_FAILURE == msDrawLabel(map, image, annopnt, annotext, layer->class[c]->labels[i], layer->scalefactor))) {
1997                 return MS_FAILURE;
1998               }
1999             }
2000         }
2001       }
2002     }
2003   }
2004   return MS_SUCCESS;
2005 }
2006 
2007 /*
2008 ** Function to render an individual shape, the style variable enables/disables the drawing of a single style
2009 ** versus a single style. This is necessary when drawing entire layers as proper overlay can only be achived
2010 ** through caching. "querymapMode" parameter is used to tell msBindLayerToShape to not override the
2011 ** QUERYMAP HILITE color.
2012 */
msDrawShape(mapObj * map,layerObj * layer,shapeObj * shape,imageObj * image,int style,int drawmode)2013 int msDrawShape(mapObj *map, layerObj *layer, shapeObj *shape, imageObj *image, int style, int drawmode)
2014 {
2015   int c,s,ret=MS_SUCCESS;
2016   shapeObj *anno_shape, *unclipped_shape = shape;
2017   int bNeedUnclippedShape = MS_FALSE;
2018   int bNeedUnclippedAnnoShape = MS_FALSE;
2019   int bShapeNeedsClipping = MS_TRUE;
2020 
2021   if(shape->numlines == 0 || shape->type == MS_SHAPE_NULL) return MS_SUCCESS;
2022 
2023   c = shape->classindex;
2024 
2025   /* Before we do anything else, we will check for a rangeitem.
2026      If its there, we need to change the style's color to map
2027      the range to the shape */
2028   for(s=0; s<layer->class[c]->numstyles; s++) {
2029     styleObj *style = layer->class[c]->styles[s];
2030     if(style->rangeitem !=  NULL)
2031       msShapeToRange((layer->class[c]->styles[s]), shape);
2032   }
2033 
2034   /* circle and point layers go through their own treatment */
2035   if(layer->type == MS_LAYER_CIRCLE) {
2036     if(msBindLayerToShape(layer, shape, drawmode) != MS_SUCCESS) return MS_FAILURE;
2037     msDrawStartShape(map, layer, image, shape);
2038     ret = circleLayerDrawShape(map,image,layer,shape);
2039     msDrawEndShape(map,layer,image,shape);
2040     return ret;
2041   } else if(layer->type == MS_LAYER_POINT || layer->type == MS_LAYER_RASTER) {
2042     if(msBindLayerToShape(layer, shape, drawmode) != MS_SUCCESS) return MS_FAILURE;
2043     msDrawStartShape(map, layer, image, shape);
2044     ret = pointLayerDrawShape(map,image,layer,shape,drawmode);
2045     msDrawEndShape(map,layer,image,shape);
2046     return ret;
2047   }
2048 
2049   if (layer->type == MS_LAYER_POLYGON && shape->type != MS_SHAPE_POLYGON) {
2050     msSetError(MS_MISCERR, "Only polygon shapes can be drawn using a polygon layer definition.", "polygonLayerDrawShape()");
2051     return (MS_FAILURE);
2052   }
2053   if (layer->type == MS_LAYER_LINE && shape->type != MS_SHAPE_POLYGON && shape->type != MS_SHAPE_LINE) {
2054     msSetError(MS_MISCERR, "Only polygon or line shapes can be drawn using a line layer definition.", "msDrawShape()");
2055     return (MS_FAILURE);
2056   }
2057 
2058   if (layer->project && layer->transform == MS_TRUE)
2059   {
2060       if( layer->reprojectorLayerToMap == NULL )
2061       {
2062           layer->reprojectorLayerToMap = msProjectCreateReprojector(
2063               &layer->projection, &map->projection);
2064         if( layer->reprojectorLayerToMap == NULL )
2065         {
2066             return MS_FAILURE;
2067         }
2068       }
2069       msProjectShapeEx(layer->reprojectorLayerToMap, shape);
2070   }
2071 
2072   /* check if we'll need the unclipped shape */
2073   if (shape->type != MS_SHAPE_POINT) {
2074     if(MS_DRAW_FEATURES(drawmode)) {
2075       for (s = 0; s < layer->class[c]->numstyles; s++) {
2076         styleObj *style = layer->class[c]->styles[s];
2077         if (style->_geomtransform.type != MS_GEOMTRANSFORM_NONE)
2078           bNeedUnclippedShape = MS_TRUE;
2079       }
2080     }
2081     /* check if we need to clip the shape */
2082     if (shape->bounds.minx < map->extent.minx ||
2083         shape->bounds.miny < map->extent.miny ||
2084         shape->bounds.maxx > map->extent.maxx ||
2085         shape->bounds.maxy > map->extent.maxy) {
2086       bShapeNeedsClipping = MS_TRUE;
2087     }
2088 
2089     if(MS_DRAW_LABELS(drawmode) && MS_DRAW_UNCLIPPED_LABELS(drawmode)) {
2090       bNeedUnclippedAnnoShape = MS_TRUE;
2091       bNeedUnclippedShape = MS_TRUE;
2092     }
2093 
2094     if(MS_DRAW_UNCLIPPED_LINES(drawmode)) {
2095       bShapeNeedsClipping = MS_FALSE;
2096     }
2097   } else {
2098     bShapeNeedsClipping = MS_FALSE;
2099   }
2100 
2101   if(layer->transform == MS_TRUE && bShapeNeedsClipping) {
2102     /* compute the size of the clipping buffer, in pixels. This buffer must account
2103      for the size of symbols drawn to avoid artifacts around the image edges */
2104     int clip_buf = 0;
2105     int s;
2106     rectObj cliprect;
2107     for (s=0;s<layer->class[c]->numstyles;s++) {
2108       double maxsize, maxunscaledsize;
2109       symbolObj *symbol;
2110       styleObj *style = layer->class[c]->styles[s];
2111       if (!MS_IS_VALID_ARRAY_INDEX(style->symbol, map->symbolset.numsymbols)) {
2112         msSetError(MS_SYMERR, "Invalid symbol index: %d", "msDrawShape()", style->symbol);
2113         return MS_FAILURE;
2114       }
2115       symbol = map->symbolset.symbol[style->symbol];
2116       if (symbol->type == MS_SYMBOL_PIXMAP) {
2117         if (MS_SUCCESS != msPreloadImageSymbol(MS_MAP_RENDERER(map), symbol))
2118           return MS_FAILURE;
2119       } else if (symbol->type == MS_SYMBOL_SVG) {
2120 #if defined(USE_SVG_CAIRO) || defined(USE_RSVG)
2121         if (MS_SUCCESS != msPreloadSVGSymbol(symbol))
2122           return MS_FAILURE;
2123 #else
2124         msSetError(MS_SYMERR, "SVG symbol support is not enabled.", "msDrawShape()");
2125         return MS_FAILURE;
2126 #endif
2127       }
2128       maxsize = MS_MAX(msSymbolGetDefaultSize(symbol), MS_MAX(style->size, style->width));
2129       maxunscaledsize = MS_MAX(style->minsize*image->resolutionfactor, style->minwidth*image->resolutionfactor);
2130       if(shape->type == MS_SHAPE_POLYGON && !IS_PARALLEL_OFFSET(style->offsety)) {
2131          maxsize += MS_MAX(fabs(style->offsety),fabs(style->offsetx));
2132       }
2133       clip_buf = MS_MAX(clip_buf,MS_NINT(MS_MAX(maxsize * layer->scalefactor, maxunscaledsize) + 1));
2134     }
2135 
2136 
2137     /* if we need a copy of the unclipped shape, transform first, then clip to avoid transforming twice */
2138     if(bNeedUnclippedShape) {
2139       msTransformShape(shape, map->extent, map->cellsize, image);
2140       if(shape->numlines == 0) return MS_SUCCESS;
2141       msComputeBounds(shape);
2142 
2143       /* TODO: there's an optimization here that can be implemented:
2144          - no need to allocate unclipped_shape for each call to this function
2145          - the calls to msClipXXXRect will discard the original lineObjs, whereas
2146            we have just copied them because they where needed. These two functions
2147            could be changed so they are instructed not to free the original lineObjs. */
2148       unclipped_shape = (shapeObj *) msSmallMalloc(sizeof (shapeObj));
2149       msInitShape(unclipped_shape);
2150       msCopyShape(shape, unclipped_shape);
2151       if(shape->type == MS_SHAPE_POLYGON) {
2152          /* #179: additional buffer for polygons */
2153          clip_buf += 2;
2154       }
2155 
2156       cliprect.minx = cliprect.miny = -clip_buf;
2157       cliprect.maxx = image->width + clip_buf;
2158       cliprect.maxy = image->height + clip_buf;
2159       if(shape->type == MS_SHAPE_POLYGON) {
2160         msClipPolygonRect(shape, cliprect);
2161       } else {
2162         assert(shape->type == MS_SHAPE_LINE);
2163         msClipPolylineRect(shape, cliprect);
2164       }
2165       if(bNeedUnclippedAnnoShape) {
2166         anno_shape = unclipped_shape;
2167       } else {
2168         anno_shape = shape;
2169       }
2170     } else {
2171       /* clip first, then transform. This means we are clipping in geographical space */
2172       double clip_buf_d;
2173       if(shape->type == MS_SHAPE_POLYGON) {
2174          /*
2175           * add a small buffer around the cliping rectangle to
2176           * avoid lines around the edges : #179
2177           */
2178          clip_buf += 2;
2179       }
2180       clip_buf_d = clip_buf * map->cellsize;
2181       cliprect.minx = map->extent.minx - clip_buf_d;
2182       cliprect.miny = map->extent.miny - clip_buf_d;
2183       cliprect.maxx = map->extent.maxx + clip_buf_d;
2184       cliprect.maxy = map->extent.maxy + clip_buf_d;
2185       if(shape->type == MS_SHAPE_POLYGON) {
2186         msClipPolygonRect(shape, cliprect);
2187       } else {
2188         assert(shape->type == MS_SHAPE_LINE);
2189         msClipPolylineRect(shape, cliprect);
2190       }
2191       msTransformShape(shape, map->extent, map->cellsize, image);
2192       msComputeBounds(shape);
2193       anno_shape = shape;
2194     }
2195 
2196   } else {
2197     /* the shape is fully in the map extent,
2198      * or is a point type layer where out of bounds points are treated differently*/
2199     if (layer->transform == MS_TRUE) {
2200       msTransformShape(shape, map->extent, map->cellsize, image);
2201       msComputeBounds(shape);
2202     } else {
2203       msOffsetShapeRelativeTo(shape, layer);
2204     }
2205     anno_shape = shape;
2206   }
2207   if(shape->numlines == 0) {
2208     ret = MS_SUCCESS; /* error message is set in msBindLayerToShape() */
2209     goto draw_shape_cleanup;
2210   }
2211 
2212   if(msBindLayerToShape(layer, shape, drawmode) != MS_SUCCESS) {
2213     ret = MS_FAILURE; /* error message is set in msBindLayerToShape() */
2214     goto draw_shape_cleanup;
2215   }
2216 
2217   switch(layer->type) {
2218     case MS_LAYER_LINE:
2219       msDrawStartShape(map, layer, image, shape);
2220       ret = lineLayerDrawShape(map, image, layer, shape, anno_shape, unclipped_shape, style, drawmode);
2221       break;
2222     case MS_LAYER_POLYGON:
2223       msDrawStartShape(map, layer, image, shape);
2224       ret = polygonLayerDrawShape(map, image, layer, shape, anno_shape, unclipped_shape, drawmode);
2225       break;
2226     case MS_LAYER_POINT:
2227     case MS_LAYER_RASTER:
2228       assert(0); //bug !
2229     default:
2230       msSetError(MS_MISCERR, "Unknown layer type.", "msDrawShape()");
2231       ret = MS_FAILURE;
2232   }
2233 
2234 draw_shape_cleanup:
2235   msDrawEndShape(map,layer,image,shape);
2236   if(unclipped_shape != shape) {
2237     msFreeShape(unclipped_shape);
2238     msFree(unclipped_shape);
2239   }
2240   return ret;
2241 }
2242 
2243 /*
2244 ** Function to render an individual point, used as a helper function for mapscript only. Since a point
2245 ** can't carry attributes you can't do attribute based font size or angle.
2246 */
msDrawPoint(mapObj * map,layerObj * layer,pointObj * point,imageObj * image,int classindex,char * labeltext)2247 int msDrawPoint(mapObj *map, layerObj *layer, pointObj *point, imageObj *image, int classindex, char *labeltext)
2248 {
2249   int s,ret;
2250   classObj *theclass=NULL;
2251   labelObj *label=NULL;
2252 
2253   if(layer->transform == MS_TRUE && layer->project && msProjectionsDiffer(&(layer->projection), &(map->projection))) {
2254     msProjectPoint(&(layer->projection), &(map->projection), point);
2255   }
2256 
2257   if(classindex > layer->numclasses) {
2258     msSetError(MS_MISCERR, "Invalid classindex (%d)", "msDrawPoint()", classindex);
2259     return MS_FAILURE;
2260   }
2261   theclass = layer->class[classindex];
2262 
2263   if(labeltext && theclass->numlabels > 0) {
2264     label = theclass->labels[0];
2265   }
2266 
2267   switch(layer->type) {
2268     case MS_LAYER_POINT:
2269       if(layer->transform == MS_TRUE) {
2270         if(!msPointInRect(point, &map->extent)) return(0);
2271         point->x = MS_MAP2IMAGE_X(point->x, map->extent.minx, map->cellsize);
2272         point->y = MS_MAP2IMAGE_Y(point->y, map->extent.maxy, map->cellsize);
2273       } else
2274         msOffsetPointRelativeTo(point, layer);
2275 
2276       for(s=0; s<theclass->numstyles; s++) {
2277         if(msScaleInBounds(map->scaledenom, theclass->styles[s]->minscaledenom, theclass->styles[s]->maxscaledenom))
2278           if(UNLIKELY(MS_FAILURE == msDrawMarkerSymbol(map, image, point, theclass->styles[s], layer->scalefactor))) {
2279             return MS_FAILURE;
2280           }
2281       }
2282       if(label && labeltext && *labeltext) {
2283         textSymbolObj *ts = msSmallMalloc(sizeof(textSymbolObj));
2284         initTextSymbol(ts);
2285         msPopulateTextSymbolForLabelAndString(ts, label, msStrdup(labeltext), layer->scalefactor, image->resolutionfactor, layer->labelcache);
2286         if(layer->labelcache) {
2287           if(msAddLabel(map, image, label, layer->index, classindex, NULL, point, -1, ts) != MS_SUCCESS) {
2288             return(MS_FAILURE);
2289           }
2290         } else {
2291           if(UNLIKELY(MS_FAILURE == msComputeTextPath(map,ts))) {
2292             freeTextSymbol(ts);
2293             free(ts);
2294             return MS_FAILURE;
2295           }
2296           ret = msDrawTextSymbol(map,image,*point,ts);
2297           freeTextSymbol(ts);
2298           free(ts);
2299           if(UNLIKELY(ret == MS_FAILURE)) return MS_FAILURE;
2300         }
2301       }
2302       break;
2303     default:
2304       break; /* don't do anything with layer of other types */
2305   }
2306 
2307   return(MS_SUCCESS); /* all done, no cleanup */
2308 }
2309 
2310 /*
2311 ** Draws a single label independently of the label cache. No collision avoidance is performed.
2312 */
msDrawLabel(mapObj * map,imageObj * image,pointObj labelPnt,char * string,labelObj * label,double scalefactor)2313 int msDrawLabel(mapObj *map, imageObj *image, pointObj labelPnt, char *string, labelObj *label, double scalefactor)
2314 {
2315   shapeObj labelPoly;
2316   label_bounds lbounds = {0};
2317   lineObj labelPolyLine;
2318   pointObj labelPolyPoints[5];
2319   textSymbolObj ts = {0};
2320   int needLabelPoly=MS_TRUE;
2321   int needLabelPoint=MS_TRUE;
2322   int haveLabelText=MS_TRUE;
2323 
2324   if(!string || !*string)
2325     haveLabelText = MS_FALSE;
2326 
2327   if(haveLabelText) {
2328     initTextSymbol(&ts);
2329     msPopulateTextSymbolForLabelAndString(&ts, label, string, scalefactor, image->resolutionfactor, 0);
2330     if(UNLIKELY(MS_FAILURE == msComputeTextPath(map,&ts))) {
2331       freeTextSymbol(&ts);
2332       return MS_FAILURE;
2333     }
2334   }
2335 
2336   labelPoly.line = &labelPolyLine; /* setup the label polygon structure */
2337   labelPoly.numlines = 1;
2338   lbounds.poly = &labelPolyLine; /* setup the label polygon structure */
2339   labelPoly.line->point = labelPolyPoints;
2340   labelPoly.line->numpoints = 5;
2341 
2342   if(label->position != MS_XY) {
2343     pointObj p = {0};
2344 
2345     if(label->numstyles > 0) {
2346       int i;
2347 
2348       for(i=0; i<label->numstyles; i++) {
2349         if(label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT || label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_NONE) {
2350           if(UNLIKELY(MS_FAILURE == msDrawMarkerSymbol(map, image, &labelPnt, label->styles[i], scalefactor))) {
2351             if(haveLabelText)
2352               freeTextSymbol(&ts);
2353             return MS_FAILURE;
2354           }
2355         } else if(haveLabelText && (label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOLY || label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELCENTER)) {
2356           if(needLabelPoly) {
2357             p = get_metrics(&labelPnt, label->position, ts.textpath, label->offsetx * ts.scalefactor,
2358                     label->offsety * ts.scalefactor, ts.rotation, 1, &lbounds);
2359             if(!lbounds.poly) {
2360               /* we need the full shape to draw the label background */
2361               labelPolyPoints[0].x = labelPolyPoints[4].x = lbounds.bbox.minx;
2362               labelPolyPoints[0].y = labelPolyPoints[4].y = lbounds.bbox.miny;
2363               labelPolyPoints[1].x = lbounds.bbox.minx;
2364               labelPolyPoints[1].y = lbounds.bbox.maxy;
2365               labelPolyPoints[2].x = lbounds.bbox.maxx;
2366               labelPolyPoints[2].y = lbounds.bbox.maxy;
2367               labelPolyPoints[3].x = lbounds.bbox.maxx;
2368               labelPolyPoints[3].y = lbounds.bbox.miny;
2369             }
2370             needLabelPoint = MS_FALSE; /* don't re-compute */
2371             needLabelPoly = MS_FALSE;
2372           }
2373           if(label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOLY) {
2374             if(UNLIKELY(MS_FAILURE == msDrawShadeSymbol(map, image, &labelPoly, label->styles[i], ts.scalefactor))) {
2375               freeTextSymbol(&ts);
2376               return MS_FAILURE;
2377             }
2378           } else {
2379             pointObj labelCenter;
2380             labelCenter.x = (lbounds.bbox.maxx + lbounds.bbox.minx)/2;
2381             labelCenter.y = (lbounds.bbox.maxy + lbounds.bbox.miny)/2;
2382             if(UNLIKELY(MS_FAILURE == msDrawMarkerSymbol(map, image, &labelCenter, label->styles[i], scalefactor))) {
2383               freeTextSymbol(&ts);
2384               return MS_FAILURE;
2385             }
2386           }
2387         } else {
2388           msSetError(MS_MISCERR,"Unknown label geomtransform %s", "msDrawLabel()",label->styles[i]->_geomtransform.string);
2389           if(haveLabelText)
2390             freeTextSymbol(&ts);
2391           return MS_FAILURE;
2392         }
2393       }
2394     }
2395 
2396     if(haveLabelText) {
2397       if(needLabelPoint)
2398         p = get_metrics(&labelPnt, label->position, ts.textpath, label->offsetx * ts.scalefactor,
2399                         label->offsety * ts.scalefactor, ts.rotation, 0, &lbounds);
2400 
2401       /* draw the label text */
2402       if(UNLIKELY(MS_FAILURE == msDrawTextSymbol(map,image,p,&ts))) {
2403         freeTextSymbol(&ts);
2404         return MS_FAILURE;
2405       }
2406     }
2407   } else {
2408     labelPnt.x += label->offsetx * ts.scalefactor;
2409     labelPnt.y += label->offsety * ts.scalefactor;
2410 
2411     if(label->numstyles > 0) {
2412       int i;
2413 
2414       for(i=0; i<label->numstyles; i++) {
2415         if(label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT ||
2416            label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_NONE) {
2417           if(UNLIKELY(MS_FAILURE == msDrawMarkerSymbol(map, image, &labelPnt, label->styles[i], scalefactor))) {
2418             freeTextSymbol(&ts);
2419             return MS_FAILURE;
2420           }
2421         } else if(haveLabelText && (label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOLY || label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELCENTER)) {
2422           if(needLabelPoly) {
2423             get_metrics(&labelPnt, label->position, ts.textpath, label->offsetx * ts.scalefactor,
2424                     label->offsety * ts.scalefactor, ts.rotation, 1, &lbounds);
2425             needLabelPoly = MS_FALSE; /* don't re-compute */
2426             if(!lbounds.poly) {
2427               /* we need the full shape to draw the label background */
2428               labelPolyPoints[0].x = labelPolyPoints[4].x = lbounds.bbox.minx;
2429               labelPolyPoints[0].y = labelPolyPoints[4].y = lbounds.bbox.miny;
2430               labelPolyPoints[1].x = lbounds.bbox.minx;
2431               labelPolyPoints[1].y = lbounds.bbox.maxy;
2432               labelPolyPoints[2].x = lbounds.bbox.maxx;
2433               labelPolyPoints[2].y = lbounds.bbox.maxy;
2434               labelPolyPoints[3].x = lbounds.bbox.maxx;
2435               labelPolyPoints[3].y = lbounds.bbox.miny;
2436             }
2437           }
2438           if(label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOLY) {
2439             if(UNLIKELY(MS_FAILURE == msDrawShadeSymbol(map, image, &labelPoly, label->styles[i], scalefactor))) {
2440               freeTextSymbol(&ts);
2441               return MS_FAILURE;
2442             }
2443           } else {
2444 	    pointObj labelCenter;
2445             labelCenter.x = (lbounds.bbox.maxx + lbounds.bbox.minx)/2;
2446             labelCenter.y = (lbounds.bbox.maxy + lbounds.bbox.miny)/2;
2447             if(UNLIKELY(MS_FAILURE == msDrawMarkerSymbol(map, image, &labelCenter, label->styles[i], scalefactor))) {
2448               freeTextSymbol(&ts);
2449               return MS_FAILURE;
2450             }
2451           }
2452         } else {
2453           msSetError(MS_MISCERR,"Unknown label geomtransform %s", "msDrawLabel()",label->styles[i]->_geomtransform.string);
2454           if(haveLabelText)
2455             freeTextSymbol(&ts);
2456           return MS_FAILURE;
2457         }
2458       }
2459     }
2460 
2461     if(haveLabelText) {
2462       /* draw the label text */
2463       if(UNLIKELY(MS_FAILURE == msDrawTextSymbol(map,image,labelPnt,&ts))) {
2464         freeTextSymbol(&ts);
2465         return MS_FAILURE;
2466       }
2467     }
2468   }
2469   if(haveLabelText)
2470     freeTextSymbol(&ts);
2471 
2472   return MS_SUCCESS;
2473 }
2474 
offset_bbox(const rectObj * from,rectObj * to,double ox,double oy)2475 static inline void offset_bbox(const rectObj *from, rectObj *to, double ox, double oy) {
2476   to->minx = from->minx + ox;
2477   to->miny = from->miny + oy;
2478   to->maxx = from->maxx + ox;
2479   to->maxy = from->maxy + oy;
2480 }
2481 
offset_label_bounds(const label_bounds * from,label_bounds * to,double ox,double oy)2482 static inline void offset_label_bounds(const label_bounds *from, label_bounds *to, double ox, double oy) {
2483   if(from->poly) {
2484     int i;
2485     for(i=0; i<from->poly->numpoints; i++) {
2486       to->poly->point[i].x = from->poly->point[i].x + ox;
2487       to->poly->point[i].y = from->poly->point[i].y + oy;
2488     }
2489     to->poly->numpoints = from->poly->numpoints;
2490   } else {
2491     to->poly = NULL;
2492   }
2493   offset_bbox(&from->bbox, &to->bbox, ox, oy);
2494 }
2495 
2496 /* private shortcut function to try a leader offsetted label
2497  * the caller must ensure that scratch->poly->points has been sufficiently allocated
2498  * to hold the points from the cachePtr's label_bounds */
offsetAndTest(mapObj * map,labelCacheMemberObj * cachePtr,double ox,double oy,int priority,int label_idx,label_bounds * scratch)2499 void offsetAndTest(mapObj *map, labelCacheMemberObj *cachePtr, double ox, double oy,
2500                    int priority, int label_idx, label_bounds *scratch)
2501 {
2502   int i,j,status;
2503   pointObj leaderpt;
2504   lineObj *scratch_line = scratch->poly;
2505 
2506   for(i=0; i<cachePtr->numtextsymbols; i++) {
2507     textSymbolObj *ts = cachePtr->textsymbols[i];
2508     if(ts->textpath) {
2509       offset_label_bounds(&ts->textpath->bounds, scratch, ox, oy);
2510       status = msTestLabelCacheCollisions(map, cachePtr, scratch, priority, label_idx);
2511       if(!status) {
2512         return;
2513       }
2514     }
2515     for(j=0; j<ts->label->numstyles; j++) {
2516       if(ts->label->styles[j]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT) {
2517         scratch->poly = scratch_line;
2518         offset_label_bounds(ts->style_bounds[j], scratch, ox, oy);
2519         status = msTestLabelCacheCollisions(map, cachePtr, scratch, priority, label_idx);
2520         if(!status) {
2521           return;
2522         }
2523       }
2524     }
2525   }
2526 
2527   leaderpt.x = cachePtr->point.x + ox;
2528   leaderpt.y = cachePtr->point.y + oy;
2529 
2530   status = msTestLabelCacheLeaderCollision(map, &cachePtr->point, &leaderpt);
2531   if(!status) {
2532     return;
2533   }
2534 
2535 
2536   /* the current offset is ok */
2537   cachePtr->leaderbbox = msSmallMalloc(sizeof(rectObj));
2538   cachePtr->leaderline = msSmallMalloc(sizeof(lineObj));
2539   cachePtr->leaderline->point = msSmallMalloc(2 * sizeof(pointObj));
2540   cachePtr->leaderline->numpoints = 2;
2541   cachePtr->leaderline->point[0] = cachePtr->point;
2542   cachePtr->leaderline->point[1] = leaderpt;
2543   cachePtr->leaderbbox->minx = MS_MIN(leaderpt.x,cachePtr->point.x);
2544   cachePtr->leaderbbox->maxx = MS_MAX(leaderpt.x,cachePtr->point.x);
2545   cachePtr->leaderbbox->miny = MS_MIN(leaderpt.y,cachePtr->point.y);
2546   cachePtr->leaderbbox->maxy = MS_MAX(leaderpt.y,cachePtr->point.y);
2547   cachePtr->status = MS_ON;
2548 
2549   offset_bbox(&cachePtr->bbox,&cachePtr->bbox,ox,oy);
2550 
2551   for(i=0; i<cachePtr->numtextsymbols; i++) {
2552     textSymbolObj *ts = cachePtr->textsymbols[i];
2553     if(ts->textpath) {
2554       offset_label_bounds(&ts->textpath->bounds, &ts->textpath->bounds, ox, oy);
2555       ts->annopoint.x += ox;
2556       ts->annopoint.y += oy;
2557     }
2558     if(ts->style_bounds) {
2559       for(j=0; j<ts->label->numstyles; j++) {
2560         if(ts->label->styles[j]->_geomtransform.type != MS_GEOMTRANSFORM_NONE)
2561           offset_label_bounds(ts->style_bounds[j], ts->style_bounds[j], ox, oy);
2562       }
2563     }
2564   }
2565 }
2566 
msDrawOffsettedLabels(imageObj * image,mapObj * map,int priority)2567 int msDrawOffsettedLabels(imageObj *image, mapObj *map, int priority)
2568 {
2569   int retval = MS_SUCCESS;
2570   int l;
2571   labelCacheObj *labelcache = &(map->labelcache);
2572   labelCacheSlotObj *cacheslot;
2573   labelCacheMemberObj *cachePtr;
2574   label_bounds scratch;
2575   lineObj scratch_line;
2576   pointObj *scratch_points = NULL;
2577   int num_allocated_scratch_points = 0;
2578   assert(MS_RENDERER_PLUGIN(image->format));
2579   cacheslot = &(labelcache->slots[priority]);
2580   scratch.poly = &scratch_line;
2581 
2582   for(l=cacheslot->numlabels-1; l>=0; l--) {
2583     cachePtr = &(cacheslot->labels[l]); /* point to right spot in the label cache */
2584     if(cachePtr->status == MS_OFF) {
2585       /* only test regular labels that have had their bounding box computed
2586        and that haven't been rendered  */
2587       classObj *classPtr = (GET_CLASS(map,cachePtr->layerindex,cachePtr->classindex));
2588       layerObj *layerPtr = (GET_LAYER(map,cachePtr->layerindex));
2589       int steps,i,num_scratch_points_to_allocate = 0;
2590 
2591       assert(classPtr->leader); /* cachePtrs that don't need to be tested have been marked as status on or delete */
2592 
2593       if(cachePtr->point.x < labelcache->gutter ||
2594           cachePtr->point.y < labelcache->gutter ||
2595           cachePtr->point.x >= image->width - labelcache->gutter ||
2596           cachePtr->point.y >= image->height - labelcache->gutter) {
2597         /* don't look for leaders if point is in edge buffer as the leader line would end up chopped off */
2598         continue;
2599       }
2600 
2601       for(i=0; i<cachePtr->numtextsymbols; i++) {
2602         int j;
2603         textSymbolObj *ts = cachePtr->textsymbols[i];
2604         if(ts->textpath && ts->textpath->bounds.poly) {
2605           num_scratch_points_to_allocate = MS_MAX(num_scratch_points_to_allocate, ts->textpath->bounds.poly->numpoints);
2606         }
2607         if(ts->style_bounds) {
2608           for(j=0;j<ts->label->numstyles; j++) {
2609             if(ts->label->styles[j]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT &&
2610                 ts->style_bounds[j]->poly) {
2611               num_scratch_points_to_allocate = MS_MAX(num_scratch_points_to_allocate, ts->style_bounds[j]->poly->numpoints);
2612             }
2613           }
2614         }
2615       }
2616       if(num_scratch_points_to_allocate > num_allocated_scratch_points) {
2617         scratch_points = msSmallRealloc(scratch_points, num_scratch_points_to_allocate * sizeof(pointObj));
2618         num_allocated_scratch_points = num_scratch_points_to_allocate;
2619       }
2620 
2621 
2622       steps = classPtr->leader->maxdistance / classPtr->leader->gridstep;
2623 
2624 #define x0 (cachePtr->point.x)
2625 #define y0 (cachePtr->point.y)
2626 #define gridstepsc (classPtr->leader->gridstep)
2627 
2628 
2629 #define otest(ox,oy) if((x0+(ox)) >= labelcache->gutter &&\
2630                 (y0+(oy)) >= labelcache->gutter &&\
2631                 (x0+(ox)) < image->width - labelcache->gutter &&\
2632                 (y0+(oy)) < image->height - labelcache->gutter) {\
2633                    scratch_line.point = scratch_points;\
2634                    scratch.poly = &scratch_line; \
2635                    offsetAndTest(map,cachePtr,(ox),(oy),priority,l,&scratch); \
2636                    if(cachePtr->status == MS_ON) break;\
2637                 }
2638 
2639       /* loop through possible offsetted positions */
2640       for(i=1; i<=steps; i++) {
2641 
2642 
2643 
2644 
2645         /* test the intermediate points on the ring */
2646 
2647         /* (points marked "0" are the ones being tested)
2648 
2649            X00X00X
2650            0XXXXX0
2651            0XXXXX0
2652            XXX.XXX
2653            0XXXXX0
2654            0XXXXX0
2655            X00X00X
2656         */
2657         int j;
2658         for(j=1; j<i-1; j++) {
2659           /* test the right positions */
2660           otest(i*gridstepsc,j * gridstepsc);
2661           otest(i*gridstepsc,- j * gridstepsc);
2662           /* test the left positions */
2663           otest(- i*gridstepsc,j * gridstepsc);
2664           otest(- i*gridstepsc,- j * gridstepsc);
2665           /* test the top positions */
2666           otest(j*gridstepsc,- i * gridstepsc);
2667           otest(- j *gridstepsc,- i * gridstepsc);
2668           /* test the bottom positions */
2669           otest(j*gridstepsc,i * gridstepsc);
2670           otest(- j *gridstepsc,i * gridstepsc);
2671         }
2672         if(j<(i-1)) break;
2673 
2674         otest(i*gridstepsc,i*gridstepsc);
2675         otest(-i*gridstepsc,-i*gridstepsc);
2676         otest(i*gridstepsc,-i*gridstepsc);
2677         otest(-i*gridstepsc,i*gridstepsc);
2678 
2679 
2680         /* test the intermediate points on the ring */
2681 
2682         /* (points marked "0" are the ones being tested)
2683 
2684            X00X00X
2685            0XXXXX0
2686            0XXXXX0
2687            XXX.XXX
2688            0XXXXX0
2689            0XXXXX0
2690            X00X00X
2691 
2692         */
2693 
2694         /* test the extreme diagonal points */
2695 
2696         /* (points marked "0" are the ones being tested)
2697 
2698            0XXXXX0
2699            XXXXXXX
2700            XXXXXXX
2701            XXX.XXX
2702            XXXXXXX
2703            XXXXXXX
2704            0XXXXX0
2705 
2706            (x0+i*gridstep, y0+i*gridstep), pos lr
2707            (x0-i*gridstep, y0-i*gridstep), pos ul
2708            (x0+i*gridstep, y0-i*gridstep), pos ur
2709            (x0-i*gridstep, y0+i*gridstep), pos ll
2710 
2711         */
2712 
2713         /* test the 4 cardinal points on the ring */
2714 
2715         /* (points marked "0" are the ones being tested)
2716 
2717            XXX0XXX
2718            XXXXXXX
2719            XXXXXXX
2720            0XX.XX0
2721            XXXXXXX
2722            XXXXXXX
2723            XXX0XXX
2724 
2725          * (x0+i*gridtep,y0), pos cr
2726 
2727          * (x0-i*gridstep,y0), pos cl
2728          * (x0,y0-i*gridstep), pos uc
2729          * (x0,y0+i*gridstep), pos lc
2730          */
2731         otest(i*gridstepsc,0);
2732         otest(-i*gridstepsc,0);
2733         otest(0,-i*gridstepsc);
2734         otest(0,i*gridstepsc);
2735       }
2736       if(cachePtr->status == MS_ON) {
2737         int ll;
2738         shapeObj labelLeader; /* label polygon (bounding box, possibly rotated) */
2739         labelLeader.line = cachePtr->leaderline; /* setup the label polygon structure */
2740         labelLeader.numlines = 1;
2741         insertRenderedLabelMember(map, cachePtr);
2742 
2743         for(ll=0; ll<classPtr->leader->numstyles; ll++) {
2744           retval = msDrawLineSymbol(map, image,&labelLeader , classPtr->leader->styles[ll], layerPtr->scalefactor);
2745           if(UNLIKELY(retval == MS_FAILURE)) {
2746             goto offset_cleanup;
2747           }
2748         }
2749         for(ll=0; ll<cachePtr->numtextsymbols; ll++) {
2750           textSymbolObj *ts = cachePtr->textsymbols[ll];
2751 
2752           if(ts->style_bounds) {
2753             /* here's where we draw the label styles */
2754             for(i=0; i<ts->label->numstyles; i++) {
2755               if(ts->label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT) {
2756                 retval = msDrawMarkerSymbol(map, image, &(labelLeader.line->point[1]), ts->label->styles[i], layerPtr->scalefactor);
2757                 if(UNLIKELY(retval == MS_FAILURE)) {
2758                   goto offset_cleanup;
2759                 }
2760               } else if(ts->label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOLY) {
2761                 retval = msDrawLabelBounds(map,image,ts->style_bounds[i],ts->label->styles[i], ts->scalefactor);
2762                 if(UNLIKELY(retval == MS_FAILURE)) {
2763                   goto offset_cleanup;
2764                 }
2765 	      } else if(ts->label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELCENTER) {
2766                 pointObj labelCenter;
2767                 labelCenter.x = (ts->style_bounds[i]->bbox.maxx + ts->style_bounds[i]->bbox.minx)/2;
2768                 labelCenter.y = (ts->style_bounds[i]->bbox.maxy + ts->style_bounds[i]->bbox.miny)/2;
2769                 retval = msDrawMarkerSymbol(map, image, &labelCenter, ts->label->styles[i], layerPtr->scalefactor);
2770                 if(UNLIKELY(retval == MS_FAILURE)) {
2771                   goto offset_cleanup;
2772                 }
2773               } else {
2774                 msSetError(MS_MISCERR,"Labels only support LABELPNT, LABELPOLY and LABELCENTER GEOMTRANSFORMS", "msDrawOffsettedLabels()");
2775                 retval = MS_FAILURE;
2776               }
2777             }
2778           }
2779           if(ts->annotext) {
2780             retval = msDrawTextSymbol(map,image,ts->annopoint,ts);
2781             if(UNLIKELY(retval == MS_FAILURE)) {
2782               goto offset_cleanup;
2783             }
2784           }
2785         }
2786         /* TODO: draw cachePtr->marker, but where ? */
2787 
2788         /*
2789          styleObj tstyle;
2790          static int foo =0;
2791          if(!foo) {
2792             srand(time(NULL));
2793             foo = 1;
2794             initStyle(&tstyle);
2795             tstyle.width = 1;
2796             tstyle.color.alpha = 255;
2797          }
2798          tstyle.color.red = random()%255;
2799          tstyle.color.green = random()%255;
2800          tstyle.color.blue =random()%255;
2801          msDrawLineSymbol(&map->symbolset, image, cachePtr->poly, &tstyle, layerPtr->scalefactor);
2802         */
2803 
2804       }
2805     }
2806   }
2807 
2808 offset_cleanup:
2809 
2810   free(scratch_points);
2811 
2812 
2813   return retval;
2814 }
2815 
fastComputeBounds(lineObj * line,rectObj * bounds)2816 void fastComputeBounds(lineObj *line, rectObj *bounds)
2817 {
2818   int j;
2819   bounds->minx = bounds->maxx = line->point[0].x;
2820   bounds->miny = bounds->maxy = line->point[0].y;
2821 
2822 
2823   for( j=0; j<line->numpoints; j++ ) {
2824     bounds->minx = MS_MIN(bounds->minx, line->point[j].x);
2825     bounds->maxx = MS_MAX(bounds->maxx, line->point[j].x);
2826     bounds->miny = MS_MIN(bounds->miny, line->point[j].y);
2827     bounds->maxy = MS_MAX(bounds->maxy, line->point[j].y);
2828   }
2829 }
2830 
computeMarkerBounds(mapObj * map,pointObj * annopoint,textSymbolObj * ts,label_bounds * poly)2831 int computeMarkerBounds(mapObj *map, pointObj *annopoint, textSymbolObj *ts, label_bounds *poly)
2832 {
2833   int i;
2834   for (i=0; i<ts->label->numstyles; i++) {
2835     styleObj *style = ts->label->styles[i];
2836     if(style->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT &&
2837         style->symbol < map->symbolset.numsymbols && style->symbol >= 0) {
2838       double sx,sy;
2839       int p;
2840       double aox,aoy;
2841       symbolObj *symbol = map->symbolset.symbol[style->symbol];
2842       if(msGetMarkerSize(map, style, &sx, &sy, ts->scalefactor) != MS_SUCCESS)
2843         return -1; /* real error, different from MS_FALSE, return -1 so we can trap it */
2844       if(style->angle) {
2845         pointObj *point = poly->poly->point;
2846         point[0].x = sx / 2.0;
2847         point[0].y = sy / 2.0;
2848         point[1].x =  point[0].x;
2849         point[1].y = -point[0].y;
2850         point[2].x = -point[0].x;
2851         point[2].y = -point[0].y;
2852         point[3].x = -point[0].x;
2853         point[3].y =  point[0].y;
2854         point[4].x =  point[0].x;
2855         point[4].y =  point[0].y;
2856         if(symbol->anchorpoint_x != 0.5 || symbol->anchorpoint_y != 0.5) {
2857           aox = (0.5 - symbol->anchorpoint_x) * sx;
2858           aoy = (0.5 - symbol->anchorpoint_y) * sy;
2859           for(p=0; p<5; p++) {
2860             point[p].x += aox;
2861             point[p].y += aoy;
2862           }
2863         }
2864         if(style->angle) {
2865           double rot = -style->angle * MS_DEG_TO_RAD;
2866           double sina = sin(rot);
2867           double cosa = cos(rot);
2868           for(p=0; p<5; p++) {
2869             double tmpx = point[p].x;
2870             point[p].x = point[p].x * cosa - point[p].y * sina;
2871             point[p].y = tmpx * sina + point[p].y * cosa;
2872           }
2873         }
2874         aox = annopoint->x + style->offsetx * ts->scalefactor;
2875         aoy = annopoint->y + style->offsety * ts->scalefactor;
2876         for(p=0; p<5; p++) {
2877           point[p].x += aox;
2878           point[p].y += aoy;
2879         }
2880         fastComputeBounds(poly->poly,&poly->bbox);
2881       } else {
2882         double aox = (0.5 - symbol->anchorpoint_x)*sx + annopoint->x + style->offsetx * ts->scalefactor;
2883         double aoy = (0.5 - symbol->anchorpoint_y)*sy + annopoint->y + style->offsety * ts->scalefactor;
2884         poly->poly = NULL;
2885         poly->bbox.maxx = sx/2.0 + aox;
2886         poly->bbox.minx = -sx/2.0 + aox;
2887         poly->bbox.maxy = sy/2.0 + aoy;
2888         poly->bbox.miny = -sy/2.0 + aoy;
2889       }
2890       break;
2891     }
2892   }
2893   if(i == ts->label->numstyles)
2894     return MS_FALSE; /* the label has no marker styles */
2895   else
2896     return MS_TRUE;
2897 }
2898 
2899 
2900 /* check that the current entry does not fall close to a label with identical text, if configured so.
2901  * Currently only checks the first label/text */
2902 
msCheckLabelMinDistance(mapObj * map,labelCacheMemberObj * lc)2903 int msCheckLabelMinDistance(mapObj *map, labelCacheMemberObj *lc)
2904 {
2905   int i;
2906   textSymbolObj *s; /* shortcut */
2907   textSymbolObj *ts;
2908   rectObj buffered;
2909   if (lc->numtextsymbols == 0)
2910     return MS_FALSE; /* no label with text */
2911   s = lc->textsymbols[0];
2912 
2913   if (!s->annotext || s->label->mindistance <= 0.0 || s->label->force == MS_TRUE)
2914     return MS_FALSE; /*  min distance is not checked */
2915 
2916   /* we buffer the label and check for intersection instead of calculating
2917      the distance of two textpaths. we also buffer only the bbox of lc for
2918      faster computation (it is still compared to the full textpath
2919      of the label cache members).
2920   */
2921   buffered = lc->bbox;
2922   buffered.minx -= s->label->mindistance * s->resolutionfactor;
2923   buffered.miny -= s->label->mindistance * s->resolutionfactor;
2924   buffered.maxx += s->label->mindistance * s->resolutionfactor;
2925   buffered.maxy += s->label->mindistance * s->resolutionfactor;
2926 
2927   for (i = 0; i < map->labelcache.num_rendered_members; i++) {
2928     labelCacheMemberObj *ilc = map->labelcache.rendered_text_symbols[i];
2929     if (ilc->numtextsymbols == 0 || !ilc->textsymbols[0]->annotext)
2930        continue;
2931 
2932     ts = ilc->textsymbols[0];
2933     if (strcmp(s->annotext, ts->annotext) != 0) {
2934       /* only check min distance against same label */
2935       continue;
2936     }
2937 
2938     if (msPointInRect(&ilc->point, &buffered) == MS_TRUE) {
2939       return MS_TRUE;
2940     }
2941 
2942     if(ts->textpath && ts->textpath->absolute) {
2943       if (intersectLabelPolygons(ts->textpath->bounds.poly, &ilc->bbox, NULL, &buffered) == MS_TRUE) {
2944         return MS_TRUE;
2945       }
2946       continue;
2947     }
2948 
2949 
2950     if (intersectLabelPolygons(NULL, &ilc->bbox, NULL, &buffered) == MS_TRUE) {
2951         return MS_TRUE;
2952     }
2953 
2954   }
2955   return MS_FALSE;
2956 }
2957 
copyLabelBounds(label_bounds * dst,label_bounds * src)2958 void copyLabelBounds(label_bounds *dst, label_bounds *src) {
2959   *dst = *src;
2960   if(src->poly) {
2961     int i;
2962     dst->poly = msSmallMalloc(sizeof(lineObj));
2963     dst->poly->numpoints = src->poly->numpoints;
2964     dst->poly->point = msSmallMalloc(dst->poly->numpoints * sizeof(pointObj));
2965     for(i=0; i<dst->poly->numpoints; i++) {
2966       dst->poly->point[i] = src->poly->point[i];
2967     }
2968   }
2969 }
2970 
getLabelPositionFromString(char * pszString)2971 static int getLabelPositionFromString(char *pszString) {
2972   if (strcasecmp(pszString, "UL")==0) return MS_UL;
2973   else if (strcasecmp(pszString, "LR")==0) return MS_LR;
2974   else if (strcasecmp(pszString, "UR")==0) return MS_UR;
2975   else if (strcasecmp(pszString, "LL")==0) return MS_LL;
2976   else if (strcasecmp(pszString, "CR")==0) return MS_CR;
2977   else if (strcasecmp(pszString, "CL")==0) return MS_CL;
2978   else if (strcasecmp(pszString, "UC")==0) return MS_UC;
2979   else if (strcasecmp(pszString, "LC")==0) return MS_LC;
2980   else return MS_CC;
2981 }
2982 
msDrawLabelCache(mapObj * map,imageObj * image)2983 int msDrawLabelCache(mapObj *map, imageObj *image)
2984 {
2985   int nReturnVal = MS_SUCCESS;
2986   struct mstimeval starttime={0}, endtime={0};
2987 
2988   if(map->debug >= MS_DEBUGLEVEL_TUNING) msGettimeofday(&starttime, NULL);
2989 
2990   if(image) {
2991     if(MS_RENDERER_PLUGIN(image->format)) {
2992       int i, l, ll, priority, its;
2993 
2994       double marker_offset_x, marker_offset_y;
2995       int label_offset_x, label_offset_y;
2996       const char *value;
2997 
2998       labelCacheMemberObj *cachePtr=NULL;
2999       layerObj *layerPtr=NULL;
3000       classObj *classPtr=NULL;
3001       textSymbolObj *textSymbolPtr=NULL;
3002 
3003       /*
3004        * some statically allocated containers for storing label bounds before
3005        * copying them into the cachePtr: we avoid allocating these structures
3006        * at runtime, except for the labels that are actually rendered.
3007        */
3008       lineObj labelpoly_line;
3009       pointObj labelpoly_points[5];
3010       label_bounds labelpoly_bounds = {0};
3011       lineObj  label_marker_line;
3012       pointObj label_marker_points[5];
3013       label_bounds label_marker_bounds;
3014       lineObj metrics_line;
3015       pointObj metrics_points[5];
3016       label_bounds metrics_bounds;
3017 
3018       label_marker_line.point = label_marker_points;
3019       label_marker_line.numpoints = 5;
3020       metrics_line.point = metrics_points;
3021       metrics_line.numpoints = 5;
3022       labelpoly_line.point = labelpoly_points;
3023       labelpoly_line.numpoints = 5;
3024 
3025       /* Look for labelcache_map_edge_buffer map metadata
3026        * If set then the value defines a buffer (in pixels) along the edge of the
3027        * map image where labels can't fall
3028        */
3029       if((value = msLookupHashTable(&(map->web.metadata), "labelcache_map_edge_buffer")) != NULL) {
3030         map->labelcache.gutter = MS_ABS(atoi(value));
3031         if(map->debug) msDebug("msDrawLabelCache(): labelcache_map_edge_buffer = %d\n", map->labelcache.gutter);
3032       }
3033 
3034       for(priority=MS_MAX_LABEL_PRIORITY-1; priority>=0; priority--) {
3035         labelCacheSlotObj *cacheslot;
3036         cacheslot = &(map->labelcache.slots[priority]);
3037 
3038         for(l=cacheslot->numlabels-1; l>=0; l--) {
3039           cachePtr = &(cacheslot->labels[l]); /* point to right spot in the label cache */
3040 
3041           layerPtr = (GET_LAYER(map, cachePtr->layerindex)); /* set a couple of other pointers, avoids nasty references */
3042           classPtr = (GET_CLASS(map, cachePtr->layerindex, cachePtr->classindex));
3043 
3044           if(cachePtr->textsymbols[0]->textpath && cachePtr->textsymbols[0]->textpath->absolute) {
3045             /* we have an angle follow label */
3046             cachePtr->bbox = cachePtr->textsymbols[0]->textpath->bounds.bbox;
3047 
3048             /* before going any futher, check that mindistance is respected */
3049             if (cachePtr->numtextsymbols && cachePtr->textsymbols[0]->label->mindistance > 0.0 && cachePtr->textsymbols[0]->annotext) {
3050               if (msCheckLabelMinDistance(map, cachePtr) == MS_TRUE) {
3051                 cachePtr->status = MS_DELETE;
3052                 MS_DEBUG(MS_DEBUGLEVEL_DEVDEBUG, map,
3053                           "Skipping labelgroup %d \"%s\" in layer \"%s\": too close to an identical label (mindistance)\n",
3054                           l, cachePtr->textsymbols[0]->annotext, layerPtr->name);
3055                 continue; /* move on to next entry, this one is too close to an already placed one */
3056               }
3057             }
3058 
3059             if(!cachePtr->textsymbols[0]->label->force)
3060               cachePtr->status = msTestLabelCacheCollisions(map,cachePtr,&cachePtr->textsymbols[0]->textpath->bounds, priority, l);
3061             else
3062               cachePtr->status = MS_ON;
3063             if(cachePtr->status) {
3064 
3065 
3066               if (UNLIKELY(MS_FAILURE == msDrawTextSymbol(map, image, cachePtr->textsymbols[0]->annopoint /*not used*/, cachePtr->textsymbols[0])))
3067               {
3068                 return MS_FAILURE;
3069               }
3070               insertRenderedLabelMember(map, cachePtr);
3071             } else {
3072               MS_DEBUG(MS_DEBUGLEVEL_DEVDEBUG,map,
3073                   "Skipping follow labelgroup %d \"%s\" in layer \"%s\": text collided\n",
3074                   l, cachePtr->textsymbols[0]->annotext, layerPtr->name);
3075             }
3076             cachePtr->status = MS_DELETE; /* we're done with this label, it won't even have a second chance in the leader phase */
3077           } else {
3078             marker_offset_x = marker_offset_y = 0; /* assume no marker */
3079 
3080             if (layerPtr->type == MS_LAYER_POINT && cachePtr->markerid!=-1) { /* there is a marker already in the image that we need to account for */
3081               markerCacheMemberObj *markerPtr = &(cacheslot->markers[cachePtr->markerid]); /* point to the right spot in the marker cache*/
3082               marker_offset_x = (markerPtr->bounds.maxx-markerPtr->bounds.minx)/2.0;
3083               marker_offset_y = (markerPtr->bounds.maxy-markerPtr->bounds.miny)/2.0;
3084             }
3085 
3086 
3087             /*
3088             ** all other cases *could* have multiple labels defined
3089             */
3090             cachePtr->status = MS_ON; /* assume this cache element can be placed */
3091             for(ll=0; ll<cachePtr->numtextsymbols; ll++) { /* RFC 77 TODO: Still may want to step through backwards... */
3092               int label_marker_status = MS_ON, have_label_marker, metrics_status = MS_ON;
3093               int need_labelpoly = 0;
3094 
3095               /* reset the lineObj which may have been unset by a previous call to get_metrics() */
3096               label_marker_bounds.poly = &label_marker_line;
3097               labelpoly_bounds.poly = &labelpoly_line;
3098 
3099               textSymbolPtr = cachePtr->textsymbols[ll];
3100               for(i=0; i<textSymbolPtr->label->numstyles; i++) {
3101                 if(textSymbolPtr->label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOLY ||
3102                    textSymbolPtr->label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELCENTER) {
3103                   need_labelpoly = 1;
3104                   break;
3105                 }
3106               }
3107 
3108               /* compute the poly of the label styles */
3109               if((have_label_marker = computeMarkerBounds(map,&cachePtr->point,textSymbolPtr, &label_marker_bounds)) == MS_TRUE) {
3110                 if(cachePtr->numtextsymbols > 1) { /* FIXME this test doesn't seem right, should probably check if we have an annotation layer with a regular style defined */
3111                   marker_offset_x = (label_marker_bounds.bbox.maxx-label_marker_bounds.bbox.minx)/2.0;
3112                   marker_offset_y = (label_marker_bounds.bbox.maxy-label_marker_bounds.bbox.miny)/2.0;
3113                 } else {
3114                   /* we might be using an old style behavior with a markerPtr */
3115                   marker_offset_x = MS_MAX(marker_offset_x,(label_marker_bounds.bbox.maxx-label_marker_bounds.bbox.minx)/2.0);
3116                   marker_offset_y = MS_MAX(marker_offset_y,(label_marker_bounds.bbox.maxy-label_marker_bounds.bbox.miny)/2.0);
3117                 }
3118                 /* add marker to cachePtr->poly */
3119                 if(textSymbolPtr->label->force != MS_TRUE) {
3120                   label_marker_status = msTestLabelCacheCollisions(map, cachePtr, &label_marker_bounds ,priority, l);
3121                 }
3122                 if(label_marker_status == MS_OFF &&
3123                     !(textSymbolPtr->label->force==MS_ON || classPtr->leader)) {
3124                   cachePtr->status = MS_DELETE;
3125                   MS_DEBUG(MS_DEBUGLEVEL_DEVDEBUG, map,
3126                       "Skipping label %d of labelgroup %d of class %d in layer \"%s\": marker collided\n",
3127                       ll, l, cachePtr->classindex, layerPtr->name);
3128                   break; /* the marker collided, break from multi-label loop */
3129                 }
3130               }
3131               if(have_label_marker == -1) return MS_FAILURE; /* error occured (symbol not found, etc...) */
3132 
3133               if(textSymbolPtr->annotext) {
3134                 /*
3135                  * if we don't have an offset defined, first check that the labelpoint itself does not collide
3136                  * this helps speed things up in dense labelling, as if the labelpoint collides there's
3137                  * no use in computing the labeltext bounds (i.e. going into shaping+freetype).
3138                  * We do however skip collision testing against the marker cache, as we want to allow rendering
3139                  * a label for points that overlap each other: this is done by setting priority to MAX_PRIORITY
3140                  * in the call to msTestLabelCacheCollisions (which is a hack!!)
3141                  */
3142                 if(!have_label_marker && textSymbolPtr->label->force != MS_TRUE && !classPtr->leader &&
3143                     !textSymbolPtr->label->offsetx && !textSymbolPtr->label->offsety) {
3144                   label_bounds labelpoint_bounds;
3145                   labelpoint_bounds.poly = NULL;
3146                   labelpoint_bounds.bbox.minx = cachePtr->point.x - 0.1;
3147                   labelpoint_bounds.bbox.maxx = cachePtr->point.x + 0.1;
3148                   labelpoint_bounds.bbox.miny = cachePtr->point.y - 0.1;
3149                   labelpoint_bounds.bbox.maxy = cachePtr->point.y + 0.1;
3150                   if(MS_OFF == msTestLabelCacheCollisions(map, cachePtr, &labelpoint_bounds, MS_MAX_LABEL_PRIORITY, l)) {
3151                     cachePtr->status = MS_DELETE; /* we won't check for leader offseted positions, as the anchor point colided */
3152                     MS_DEBUG(MS_DEBUGLEVEL_DEVDEBUG, map,
3153                         "Skipping label %d \"%s\" of labelgroup %d of class %d in layer \"%s\": labelpoint collided\n",
3154                         ll, textSymbolPtr->annotext, l, cachePtr->classindex, layerPtr->name);
3155                     break;
3156                   }
3157                 }
3158 
3159                 /* compute label size */
3160                 if(!textSymbolPtr->textpath) {
3161                   if(UNLIKELY(MS_FAILURE == msComputeTextPath(map,textSymbolPtr))) {
3162                     return MS_FAILURE;
3163                   }
3164                 }
3165 
3166                 /* if our label has an outline, adjust the marker offset so the outlinecolor does
3167                  * not bleed into the marker */
3168                 if(marker_offset_x && MS_VALID_COLOR(textSymbolPtr->label->outlinecolor)) {
3169                   marker_offset_x += textSymbolPtr->label->outlinewidth/2.0 * textSymbolPtr->scalefactor;
3170                   marker_offset_y += textSymbolPtr->label->outlinewidth/2.0 * textSymbolPtr->scalefactor;
3171                 }
3172 
3173                 /* apply offset and buffer settings */
3174                 if(textSymbolPtr->label->anglemode != MS_FOLLOW) {
3175                   label_offset_x = textSymbolPtr->label->offsetx * textSymbolPtr->scalefactor;
3176                   label_offset_y = textSymbolPtr->label->offsety * textSymbolPtr-> scalefactor;
3177                 } else {
3178                   label_offset_x = 0;
3179                   label_offset_y = 0;
3180                 }
3181 
3182                 if(textSymbolPtr->label->position == MS_AUTO) {
3183                   /* no point in using auto positionning if the marker cannot be placed */
3184                   int positions[MS_POSITIONS_LENGTH], npositions=0;
3185 
3186                   /*
3187                   **   (Note: might be able to re-order this for more speed.)
3188                   */
3189 		  if(msLayerGetProcessingKey(layerPtr, "LABEL_POSITIONS")) {
3190                     int p, ncustom_positions=0;
3191                     char **custom_positions = msStringSplitComplex(msLayerGetProcessingKey(layerPtr, "LABEL_POSITIONS"), ",", &ncustom_positions, MS_STRIPLEADSPACES|MS_STRIPENDSPACES);
3192                     for(p=0; p<MS_MIN(9,ncustom_positions); p++)
3193                       positions[p] = getLabelPositionFromString(custom_positions[p]);
3194                     npositions = p;
3195                     msFree(custom_positions);
3196 		  } else if(layerPtr->type == MS_LAYER_POLYGON && marker_offset_x==0 ) {
3197                     positions[0]=MS_CC;
3198                     positions[1]=MS_UC;
3199                     positions[2]=MS_LC;
3200                     positions[3]=MS_CL;
3201                     positions[4]=MS_CR;
3202                     npositions = 5;
3203                   } else if(layerPtr->type == MS_LAYER_LINE && marker_offset_x == 0) {
3204                     positions[0]=MS_UC;
3205                     positions[1]=MS_LC;
3206                     positions[2]=MS_CC;
3207                     npositions = 3;
3208                   } else {
3209                     positions[0]=MS_UL;
3210                     positions[1]=MS_LR;
3211                     positions[2]=MS_UR;
3212                     positions[3]=MS_LL;
3213                     positions[4]=MS_CR;
3214                     positions[5]=MS_CL;
3215                     positions[6]=MS_UC;
3216                     positions[7]=MS_LC;
3217                     npositions = 8;
3218                   }
3219 
3220                   for(i=0; i<npositions; i++) {
3221                     // RFC 77 TODO: take label_marker_offset_x/y into account
3222                     metrics_bounds.poly = &metrics_line;
3223                     textSymbolPtr->annopoint = get_metrics(&(cachePtr->point), positions[i], textSymbolPtr->textpath,
3224                                                            marker_offset_x + label_offset_x, marker_offset_y + label_offset_y,
3225                                                            textSymbolPtr->rotation, textSymbolPtr->label->buffer * textSymbolPtr->scalefactor, &metrics_bounds);
3226                     if(textSymbolPtr->label->force == MS_OFF) {
3227                       for(its=0;its<ll;its++) {
3228                         /* check for collisions inside the label group */
3229                         if(intersectTextSymbol(cachePtr->textsymbols[its],&metrics_bounds) == MS_TRUE) {
3230                           /* there was a self intersection */
3231                           break; /* next position, will break out to next position in containing loop*/
3232                         }
3233                         if(its != ll)
3234                           continue; /* goto next position, this one had an intersection with our own label group */
3235                       }
3236                     }
3237 
3238                     metrics_status = msTestLabelCacheCollisions(map, cachePtr,&metrics_bounds, priority, l);
3239 
3240                     /* found a suitable place for this label */
3241                     if(metrics_status == MS_TRUE || (i==(npositions-1) && textSymbolPtr->label->force == MS_ON)) {
3242                       metrics_status = MS_TRUE; /* set to true in case we are forcing it */
3243                       /* compute anno poly for label background if needed */
3244                       if(need_labelpoly) get_metrics(&(cachePtr->point), positions[i], textSymbolPtr->textpath,
3245                                                                 marker_offset_x + label_offset_x, marker_offset_y + label_offset_y,
3246                                                                 textSymbolPtr->rotation, 1, &labelpoly_bounds);
3247 
3248                       break; /* ...out of position loop */
3249                     }
3250                   } /* next position */
3251 
3252                   /* if position auto didn't manage to find a position, but we have leader configured
3253                    * for the class, then we want to compute the label poly anyway, placed as MS_CC */
3254                   if(classPtr->leader && metrics_status == MS_FALSE) {
3255                     metrics_bounds.poly = &metrics_line;
3256                     textSymbolPtr->annopoint = get_metrics(&(cachePtr->point), MS_CC, textSymbolPtr->textpath, label_offset_x, label_offset_y,
3257                                                            textSymbolPtr->rotation, textSymbolPtr->label->buffer * textSymbolPtr->scalefactor,
3258                                                            &metrics_bounds);
3259                     if(need_labelpoly) get_metrics(&(cachePtr->point), MS_CC, textSymbolPtr->textpath,
3260                                                               label_offset_x, label_offset_y,
3261                                                               textSymbolPtr->rotation, 1, &labelpoly_bounds);
3262                   }
3263                 } else { /* explicit position */
3264                   if(textSymbolPtr->label->position == MS_CC) { /* don't need the marker_offset */
3265                     metrics_bounds.poly = &metrics_line;
3266                     textSymbolPtr->annopoint = get_metrics(&(cachePtr->point), MS_CC, textSymbolPtr->textpath, label_offset_x, label_offset_y,
3267                             textSymbolPtr->rotation, textSymbolPtr->label->buffer * textSymbolPtr->scalefactor, &metrics_bounds);
3268                     if(need_labelpoly) get_metrics(&(cachePtr->point), MS_CC, textSymbolPtr->textpath,
3269                                                               label_offset_x, label_offset_y, textSymbolPtr->rotation, 1, &labelpoly_bounds);
3270                   } else {
3271                     metrics_bounds.poly = &metrics_line;
3272                     textSymbolPtr->annopoint = get_metrics(&(cachePtr->point), textSymbolPtr->label->position, textSymbolPtr->textpath,
3273                                                            marker_offset_x + label_offset_x, marker_offset_y + label_offset_y,
3274                                                            textSymbolPtr->rotation, textSymbolPtr->label->buffer * textSymbolPtr->scalefactor,
3275                                                            &metrics_bounds);
3276                     if(need_labelpoly) get_metrics(&(cachePtr->point), textSymbolPtr->label->position, textSymbolPtr->textpath,
3277                                                               marker_offset_x + label_offset_x, marker_offset_y + label_offset_y, textSymbolPtr->rotation, 1, &labelpoly_bounds);
3278                   }
3279 
3280                   if(textSymbolPtr->label->force == MS_ON) {
3281                     metrics_status = MS_ON;
3282                   } else {
3283                     if(textSymbolPtr->label->force == MS_OFF) {
3284                       /* check for collisions inside the label group unless the label is FORCE GROUP */
3285                       for(its=0;its<ll;its++) {
3286                         /* check for collisions inside the label group */
3287                         if(intersectTextSymbol(cachePtr->textsymbols[its],&metrics_bounds) == MS_TRUE) {
3288                           /* there was a self intersection */
3289                           break; /* will break out to next position in containing loop*/
3290                         }
3291                         if(its != ll) {
3292                           cachePtr->status = MS_DELETE; /* TODO RFC98: check if this is correct */
3293                           MS_DEBUG(MS_DEBUGLEVEL_DEVDEBUG,map,
3294                               "Skipping label %d (\"%s\") of labelgroup %d of class %d in layer \"%s\": intercollision with label text inside labelgroup\n",
3295                               ll, textSymbolPtr->annotext, l,cachePtr->classindex, layerPtr->name);
3296                           break; /* collision within the group, break out of textSymbol loop */
3297                         }
3298                       }
3299                     }
3300                     /* TODO: in case we have leader lines and multiple labels, there's no use in testing for labelcache collisions
3301                     * once a first collision has been found. we only need to know that the label group has collided, and the
3302                     * poly of the whole label group: if(label_group) testLabelCacheCollisions */
3303                     metrics_status = msTestLabelCacheCollisions(map, cachePtr,&metrics_bounds, priority, l);
3304                   }
3305                 } /* end POSITION AUTO vs Fixed POSITION */
3306 
3307                 if(!metrics_status && classPtr->leader == 0) {
3308                   cachePtr->status = MS_DELETE;
3309                   MS_DEBUG(MS_DEBUGLEVEL_DEVDEBUG,map,
3310                       "Skipping label %d \"%s\" of labelgroup %d of class %d in layer \"%s\": text collided\n",
3311                       ll, textSymbolPtr->annotext, l, cachePtr->classindex, layerPtr->name);
3312                   break; /* no point looking at more labels, unless their is a leader defined */
3313                 }
3314               }
3315 
3316               /* if we're here, we can either fit the label directly, or we need to put it in the leader queue */
3317               assert((metrics_status && label_marker_status) || classPtr->leader);
3318 
3319               if(textSymbolPtr->annotext) {
3320                 copyLabelBounds(&textSymbolPtr->textpath->bounds, &metrics_bounds);
3321               }
3322               if(have_label_marker) {
3323                 if(!textSymbolPtr->style_bounds)
3324                   textSymbolPtr->style_bounds = msSmallCalloc(textSymbolPtr->label->numstyles, sizeof(label_bounds*));
3325                 for(its=0;its<textSymbolPtr->label->numstyles; its++) {
3326                   if(textSymbolPtr->label->styles[its]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT) {
3327                     textSymbolPtr->style_bounds[its] = msSmallMalloc(sizeof(label_bounds));
3328                     copyLabelBounds(textSymbolPtr->style_bounds[its], &label_marker_bounds);
3329                   }
3330                 }
3331               }
3332               if(!label_marker_status || ! metrics_status) {
3333                 MS_DEBUG(MS_DEBUGLEVEL_DEVDEBUG, map,
3334                     "Putting label %d of labelgroup %d of class %d , layer \"%s\" in leader queue\n",
3335                     ll, l, cachePtr->classindex, layerPtr->name);
3336                 cachePtr->status = MS_OFF; /* we have a collision, but this entry is a candidate for leader testing */
3337               }
3338 
3339               /* do we need to copy the labelpoly, or can we use the static one ?*/
3340               if(cachePtr->numtextsymbols > 1 || (cachePtr->status == MS_OFF && classPtr->leader)) {
3341                 /*
3342                  * if we have more than one label, or if we have a single one which didn't fit but needs
3343                  * to go through leader offset testing
3344                  */
3345                 if(!textSymbolPtr->style_bounds)
3346                   textSymbolPtr->style_bounds = msSmallCalloc(textSymbolPtr->label->numstyles, sizeof(label_bounds*));
3347                 for(its=0;its<textSymbolPtr->label->numstyles; its++) {
3348                   if(textSymbolPtr->label->styles[its]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOLY ||
3349                      textSymbolPtr->label->styles[its]->_geomtransform.type == MS_GEOMTRANSFORM_LABELCENTER) {
3350                     textSymbolPtr->style_bounds[its] = msSmallMalloc(sizeof(label_bounds));
3351                     copyLabelBounds(textSymbolPtr->style_bounds[its], &labelpoly_bounds);
3352                   }
3353                 }
3354 
3355               } /* else: we'll use labelpoly_bounds directly below */
3356             } /* next label in the group */
3357 
3358             if(cachePtr->status != MS_DELETE) {
3359               /* compute the global label bbox */
3360               int inited = 0,s;
3361               for(its=0;its<cachePtr->numtextsymbols;its++) {
3362                 if(cachePtr->textsymbols[its]->annotext) {
3363                   if(inited == 0) {
3364                     cachePtr->bbox = cachePtr->textsymbols[its]->textpath->bounds.bbox;
3365                     inited = 1;
3366                   } else {
3367                     cachePtr->bbox.minx = MS_MIN(cachePtr->bbox.minx, cachePtr->textsymbols[its]->textpath->bounds.bbox.minx);
3368                     cachePtr->bbox.miny = MS_MIN(cachePtr->bbox.miny, cachePtr->textsymbols[its]->textpath->bounds.bbox.miny);
3369                     cachePtr->bbox.maxx = MS_MAX(cachePtr->bbox.maxx, cachePtr->textsymbols[its]->textpath->bounds.bbox.maxx);
3370                     cachePtr->bbox.maxy = MS_MAX(cachePtr->bbox.maxy, cachePtr->textsymbols[its]->textpath->bounds.bbox.maxy);
3371                   }
3372                 }
3373                 for(s=0; s<cachePtr->textsymbols[its]->label->numstyles; s++) {
3374                   if(cachePtr->textsymbols[its]->label->styles[s]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT) {
3375                     if(inited == 0) {
3376                       cachePtr->bbox = cachePtr->textsymbols[its]->style_bounds[s]->bbox;
3377                       inited = 1;
3378                       break;
3379                     } else {
3380                       cachePtr->bbox.minx = MS_MIN(cachePtr->bbox.minx, cachePtr->textsymbols[its]->style_bounds[s]->bbox.minx);
3381                       cachePtr->bbox.miny = MS_MIN(cachePtr->bbox.miny, cachePtr->textsymbols[its]->style_bounds[s]->bbox.miny);
3382                       cachePtr->bbox.maxx = MS_MAX(cachePtr->bbox.maxx, cachePtr->textsymbols[its]->style_bounds[s]->bbox.maxx);
3383                       cachePtr->bbox.maxy = MS_MAX(cachePtr->bbox.maxy, cachePtr->textsymbols[its]->style_bounds[s]->bbox.maxy);
3384                       break;
3385                     }
3386                   }
3387                 }
3388               }
3389             }
3390 
3391             /* check that mindistance is respected */
3392             if (cachePtr->numtextsymbols && cachePtr->textsymbols[0]->label->mindistance > 0.0 && cachePtr->textsymbols[0]->annotext) {
3393               if (msCheckLabelMinDistance(map, cachePtr) == MS_TRUE) {
3394                 cachePtr->status = MS_DELETE;
3395                 MS_DEBUG(MS_DEBUGLEVEL_DEVDEBUG, map,
3396                          "Skipping labelgroup %d \"%s\" in layer \"%s\": too close to an identical label (mindistance)\n",
3397                          l, cachePtr->textsymbols[0]->annotext, layerPtr->name);
3398               }
3399             }
3400 
3401             if(cachePtr->status == MS_OFF || cachePtr->status == MS_DELETE)
3402               continue; /* next labelCacheMemberObj, as we had a collision */
3403 
3404             /* insert the rendered label */
3405             insertRenderedLabelMember(map, cachePtr);
3406 
3407             for(ll=0; ll<cachePtr->numtextsymbols; ll++) {
3408               textSymbolPtr = cachePtr->textsymbols[ll];
3409 
3410               /* here's where we draw the label styles */
3411                 for(i=0; i<textSymbolPtr->label->numstyles; i++) {
3412                   if(textSymbolPtr->label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT) {
3413                     if(UNLIKELY(MS_FAILURE == msDrawMarkerSymbol(map, image, &(cachePtr->point), textSymbolPtr->label->styles[i], textSymbolPtr->scalefactor))) {
3414                       return MS_FAILURE;
3415                     }
3416                   } else if(textSymbolPtr->annotext && textSymbolPtr->label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOLY) {
3417                     if(textSymbolPtr->style_bounds && textSymbolPtr->style_bounds[i]) {
3418                       if(UNLIKELY(MS_FAILURE == msDrawLabelBounds(map,image,textSymbolPtr->style_bounds[i],textSymbolPtr->label->styles[i], textSymbolPtr->scalefactor))) {
3419                         return MS_FAILURE;
3420                       }
3421                     } else {
3422                       if(UNLIKELY(MS_FAILURE == msDrawLabelBounds(map,image,&labelpoly_bounds,textSymbolPtr->label->styles[i], textSymbolPtr->scalefactor))) {
3423                         return MS_FAILURE;
3424                       }
3425                     }
3426 		  } else if(textSymbolPtr->annotext && textSymbolPtr->label->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELCENTER) {
3427                     pointObj labelCenter;
3428 
3429                     if(textSymbolPtr->style_bounds && textSymbolPtr->style_bounds[i]) {
3430                       labelCenter.x = (textSymbolPtr->style_bounds[i]->bbox.maxx + textSymbolPtr->style_bounds[i]->bbox.minx)/2;
3431                       labelCenter.y = (textSymbolPtr->style_bounds[i]->bbox.maxy + textSymbolPtr->style_bounds[i]->bbox.miny)/2;
3432                       if(UNLIKELY(MS_FAILURE == msDrawMarkerSymbol(map,image,&labelCenter,textSymbolPtr->label->styles[i], textSymbolPtr->scalefactor))) {
3433                         return MS_FAILURE;
3434                       }
3435                     } else {
3436                       labelCenter.x = (labelpoly_bounds.bbox.maxx + labelpoly_bounds.bbox.minx)/2;
3437                       labelCenter.y = (labelpoly_bounds.bbox.maxy + labelpoly_bounds.bbox.miny)/2;
3438                       if(UNLIKELY(MS_FAILURE == msDrawMarkerSymbol(map,image,&labelCenter,textSymbolPtr->label->styles[i], textSymbolPtr->scalefactor))) {
3439                         return MS_FAILURE;
3440                       }
3441                     }
3442 
3443                   } else {
3444                     msSetError(MS_MISCERR,"Labels only support LABELPNT, LABELPOLY and LABELCENTER GEOMTRANSFORMS", "msDrawLabelCache()");
3445                     return MS_FAILURE;
3446                   }
3447                 }
3448 
3449               if(textSymbolPtr->annotext) {
3450                 if(UNLIKELY(MS_FAILURE == msDrawTextSymbol(map,image,textSymbolPtr->annopoint,textSymbolPtr))) {
3451                   return MS_FAILURE;
3452                 }
3453               }
3454             }
3455           }
3456         } /* next label(group) from cacheslot */
3457         if(UNLIKELY(MS_FAILURE == msDrawOffsettedLabels(image, map, priority))) {
3458           return MS_FAILURE;
3459         }
3460       } /* next priority */
3461 #ifdef TBDEBUG
3462       styleObj tstyle;
3463       initStyle(&tstyle);
3464       tstyle.width = 1;
3465       tstyle.color.alpha = 255;
3466       tstyle.color.red = 255;
3467       tstyle.color.green = 0;
3468       tstyle.color.blue = 0;
3469       for(priority=MS_MAX_LABEL_PRIORITY-1; priority>=0; priority--) {
3470         labelCacheSlotObj *cacheslot;
3471         cacheslot = &(map->labelcache.slots[priority]);
3472 
3473         for(l=cacheslot->numlabels-1; l>=0; l--) {
3474           cachePtr = &(cacheslot->labels[l]); /* point to right spot in the label cache */
3475           /*
3476           assert((cachePtr->poly == NULL && cachePtr->status == MS_OFF) ||
3477                   (cachePtr->poly && (cachePtr->status == MS_ON));
3478            */
3479           if(cachePtr->status) {
3480             msDrawLineSymbol(map, image, cachePtr->poly, &tstyle, layerPtr->scalefactor);
3481           }
3482         }
3483       }
3484 #endif
3485 
3486       nReturnVal = MS_SUCCESS; /* necessary? */
3487     }
3488   }
3489 
3490   if(map->debug >= MS_DEBUGLEVEL_TUNING) {
3491     msGettimeofday(&endtime, NULL);
3492     msDebug("msDrawMap(): Drawing Label Cache, %.3fs\n",
3493             (endtime.tv_sec+endtime.tv_usec/1.0e6)-
3494             (starttime.tv_sec+starttime.tv_usec/1.0e6) );
3495   }
3496 
3497   return nReturnVal;
3498 }
3499 
3500 
3501 /**
3502  * Generic function to tell the underline device that layer
3503  * drawing is stating
3504  */
3505 
msImageStartLayer(mapObj * map,layerObj * layer,imageObj * image)3506 void msImageStartLayer(mapObj *map, layerObj *layer, imageObj *image)
3507 {
3508   if (image) {
3509     if( MS_RENDERER_PLUGIN(image->format) ) {
3510       char *approximation_scale = msLayerGetProcessingKey( layer, "APPROXIMATION_SCALE" );
3511       if(approximation_scale) {
3512         if(!strncasecmp(approximation_scale,"ROUND",5)) {
3513           MS_IMAGE_RENDERER(image)->transform_mode = MS_TRANSFORM_ROUND;
3514         } else if(!strncasecmp(approximation_scale,"FULL",4)) {
3515           MS_IMAGE_RENDERER(image)->transform_mode = MS_TRANSFORM_FULLRESOLUTION;
3516         } else if(!strncasecmp(approximation_scale,"SIMPLIFY",8)) {
3517           MS_IMAGE_RENDERER(image)->transform_mode = MS_TRANSFORM_SIMPLIFY;
3518         } else {
3519           MS_IMAGE_RENDERER(image)->transform_mode = MS_TRANSFORM_SNAPTOGRID;
3520           MS_IMAGE_RENDERER(image)->approximation_scale = atof(approximation_scale);
3521         }
3522       } else {
3523         MS_IMAGE_RENDERER(image)->transform_mode = MS_IMAGE_RENDERER(image)->default_transform_mode;
3524         MS_IMAGE_RENDERER(image)->approximation_scale = MS_IMAGE_RENDERER(image)->default_approximation_scale;
3525       }
3526       MS_IMAGE_RENDERER(image)->startLayer(image, map, layer);
3527     } else if( MS_RENDERER_IMAGEMAP(image->format) )
3528       msImageStartLayerIM(map, layer, image);
3529 
3530   }
3531 }
3532 
3533 
3534 /**
3535  * Generic function to tell the underline device that layer
3536  * drawing is ending
3537  */
3538 
msImageEndLayer(mapObj * map,layerObj * layer,imageObj * image)3539 void msImageEndLayer(mapObj *map, layerObj *layer, imageObj *image)
3540 {
3541   if(image) {
3542     if( MS_RENDERER_PLUGIN(image->format) ) {
3543       MS_IMAGE_RENDERER(image)->endLayer(image,map,layer);
3544     }
3545   }
3546 }
3547 
3548 /**
3549  * Generic function to tell the underline device that shape
3550  * drawing is starting
3551  */
3552 
msDrawStartShape(mapObj * map,layerObj * layer,imageObj * image,shapeObj * shape)3553 void msDrawStartShape(mapObj *map, layerObj *layer, imageObj *image,
3554                       shapeObj *shape)
3555 {
3556   if (image) {
3557     if(MS_RENDERER_PLUGIN(image->format)) {
3558       if (image->format->vtable->startShape)
3559         image->format->vtable->startShape(image, shape);
3560     }
3561 
3562 
3563   }
3564 }
3565 
3566 
3567 /**
3568  * Generic function to tell the underline device that shape
3569  * drawing is ending
3570  */
3571 
msDrawEndShape(mapObj * map,layerObj * layer,imageObj * image,shapeObj * shape)3572 void msDrawEndShape(mapObj *map, layerObj *layer, imageObj *image,
3573                     shapeObj *shape)
3574 {
3575   if(MS_RENDERER_PLUGIN(image->format)) {
3576     if (image->format->vtable->endShape)
3577       image->format->vtable->endShape(image, shape);
3578   }
3579 }
3580 /**
3581  * take the value from the shape and use it to change the
3582  * color in the style to match the range map
3583  */
msShapeToRange(styleObj * style,shapeObj * shape)3584 int msShapeToRange(styleObj *style, shapeObj *shape)
3585 {
3586   double fieldVal;
3587   char* fieldStr;
3588 
3589   /*first, get the value of the rangeitem, which should*/
3590   /*evaluate to a double*/
3591   fieldStr = shape->values[style->rangeitemindex];
3592   if (fieldStr == NULL) { /*if there's not value, bail*/
3593     return MS_FAILURE;
3594   }
3595   fieldVal = 0.0;
3596   fieldVal = atof(fieldStr); /*faith that it's ok -- */
3597   /*should switch to strtod*/
3598   return msValueToRange(style, fieldVal, MS_COLORSPACE_RGB);
3599 }
3600 
3601 /**
3602  * Allow direct mapping of a value so that mapscript can use the
3603  * Ranges.  The styls passed in is updated to reflect the right color
3604  * based on the fieldVal
3605  */
msValueToRange(styleObj * style,double fieldVal,colorspace cs)3606 int msValueToRange(styleObj *style, double fieldVal, colorspace cs)
3607 {
3608   double range;
3609   double scaledVal;
3610 
3611   range = style->maxvalue - style->minvalue;
3612   scaledVal = (fieldVal - style->minvalue)/range;
3613 
3614   if(cs == MS_COLORSPACE_RGB) {
3615     /*At this point, we know where on the range we need to be*/
3616     /*However, we don't know how to map it yet, since RGB(A) can */
3617     /*Go up or down*/
3618     style->color.red = (int)(MS_MAX(0,(MS_MIN(255, (style->mincolor.red + ((style->maxcolor.red - style->mincolor.red) * scaledVal))))));
3619     style->color.green = (int)(MS_MAX(0,(MS_MIN(255,(style->mincolor.green + ((style->maxcolor.green - style->mincolor.green) * scaledVal))))));
3620     style->color.blue = (int)(MS_MAX(0,(MS_MIN(255,(style->mincolor.blue + ((style->maxcolor.blue - style->mincolor.blue) * scaledVal))))));
3621     style->color.alpha = (int)(MS_MAX(0,(MS_MIN(255,(style->mincolor.alpha + ((style->maxcolor.alpha - style->mincolor.alpha) * scaledVal))))));
3622   } else {
3623     /* HSL */
3624     assert(cs == MS_COLORSPACE_HSL);
3625     if(fieldVal <= style->minvalue) style->color = style->mincolor;
3626     else if(fieldVal >= style->maxvalue) style->color = style->maxcolor;
3627     else {
3628       double mh,ms,ml,Mh,Ms,Ml;
3629       msRGBtoHSL(&style->mincolor,&mh,&ms,&ml);
3630       msRGBtoHSL(&style->maxcolor,&Mh,&Ms,&Ml);
3631       mh+=(Mh-mh)*scaledVal;
3632       ms+=(Ms-ms)*scaledVal;
3633       ml+=(Ml-ml)*scaledVal;
3634       msHSLtoRGB(mh,ms,ml,&style->color);
3635       style->color.alpha = style->mincolor.alpha + (style->maxcolor.alpha - style->mincolor.alpha)*scaledVal;
3636     }
3637   }
3638   /*( "msMapRange(): %i %i %i", style->color.red , style->color.green, style->color.blue);*/
3639 
3640 #if ALPHACOLOR_ENABLED
3641   /*NO ALPHA RANGE YET  style->color.alpha = style->mincolor.alpha + ((style->maxcolor.alpha - style->mincolor.alpha) * scaledVal);*/
3642 #endif
3643 
3644   return MS_SUCCESS;
3645 }
3646