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