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